Python decorators simply allow you to wrap a function call with one or more function calls. The idea is that you can write a decorator to modify (i.e., decorate) the behavior of many like functions. For example, in CherryPy I use a decorator on exposed class methods to wrap pages of content with an HTML header and footer.
For the purposes of this post, lets take a simple example.
This outputs:
def print_hello(name):
print "Hello, ", name
def print_goodbye(name):
print "Goodbye, ", name
print_hello("Matt")
print_goodbye("Matt")
Hello, MattSimple, right?
Okay, so maybe we want to gussy up our function, say by surrounding both print statements with a string of twenty dashes. We could modify each function like:
def print_goodbye2(name):Producing:
print "-" * 20
print "Goodbye, ", name
print "-" * 20
print_goodbye2("Matt")
--------------------Of course to do this, we have to change both functions. What if we want to write the dash-output code and apply it to each function? This is where we can use a decorator!
Goodbye, Matt
--------------------
In the following code, we create a decorator function called
add_lines. It takes a function as an argument (the function its decorating), and returns a function _decor, which takes the arguments passed to the function it is decorating.Inside the _decor closure, we print a dashed line, call the decorated function (with its expected arguments), and print another dashed line. Then, we define our functions
print_hello and print_goodbye and we put @add_lines before both funtion definitions. This tells Python that we are using add_lines to decorate the following function.def add_lines(fn):The result of calling our decorated functions is:
def _decor(*args):
print "-" * 20
fn(*args)
print "-" * 20
return _decor
#end add_lines
@add_lines
def print_hello(name):
print "Hello, ", name
@add_lines
def print_goodbye(name):
print "Goodbye, ", name
print_hello("Matt")
print_goodbye("Matt")
--------------------So, when you call
Hello, Matt
--------------------
--------------------
Goodbye, Matt
--------------------
print_hello or print_goodbye you are effectively calling add_lines which in turn calls the original function.You can also pass arguments to decorators using an additional closure:
def add_char_lines(char): #function with expected argumentsProduces:
def _wrap(fn): #closure that receives the decorated function
def _decor(*args): #closure that receives the decorated function's args
print char * 20
fn(*args)
print char * 20
return _decor
return _wrap
#end add_char_lines
@add_char_lines("*")
def print_hello(name):
print "Hello, ", name
print_hello("Matt")
********************Finally, you can also apply multiple decorators to a function. The following works, even if it is an unlikely example:
Hello, Matt
********************
def add_lines(fn):Which produces:
def _decor(*args):
print "-" * 20
fn(*args)
print "-" * 20
return _decor
#end add_lines
def lower(fn):
def _decor(*args):
nargs = []
for arg in args:
if type(arg) == type(""):
nargs.append(arg.lower())
fn(*nargs)
return _decor
#end lower
@add_lines
@lower
def print_string(a_string):
print "STRING: ", a_string
print_string("A STRING")
--------------------The Python wiki takes a much more in-depth look at decorators.
STRING: a string
--------------------
No comments:
Post a Comment