Reputation: 11
I am confused by the calling sequence of the following code piece.
#/usr/bin/python
def decorator_no_args(fn):
if not callable(fn):
raise TypeError
def wrapper():
return fn()
print "decorator_no_args"
return wrapper
@decorator_no_args
def foo_no_args():
print "foo_no_args"
def decorator_args(* args, **kargs):
def wrapper0(fn):
def wrapper1(*args, **kargs):
return fn(*args, **kargs)
return wrapper1
print "decorator_args"
return wrapper0
@decorator_args(1)
def foo_args(arg0):
print "foo_args"
if __name__ == "__main__":
foo_no_args()
The output is:
decorator_no_args
decorator_args
foo_no_args
Function decorator_args()
isn't called as you can see above. But why is it recorded?
Upvotes: 0
Views: 471
Reputation: 1121824
2 things: decorators are applied when the function definition is executed, and the @expression
syntax takes an expression; it is evaluated when decorating.
The line @decorator_args(1)
calls the decorator_args()
function, passing in 1
as an argument. The result of that call is used as the decorator.
Decorators are really just syntactic sugar, this:
@decorator_args(1)
def foo_args(arg0):
print "foo_args"
is really executed as:
def foo_args(arg0):
print "foo_args"
foo_args = decorator_args(1)(foo_args)
Note that decorator_args(1)
is called. And it's return value is called again, to produce a decorated result.
When you look at decorator_args()
you see it returns a decorator:
def decorator_args(* args, **kargs):
def wrapper0(fn):
def wrapper1(*args, **kargs):
return fn(*args, **kargs)
return wrapper1
print "decorator_args"
return wrapper0
wrapper0
is the real decorator; it is returned when decorator_args()
is called. foo_args()
is passed to wrapper0()
(as the fn
argument), and the original function is replaced by wrapper1
.
It doesn't matter at all that foo_args
is never called; decorators are not applied each time when you call the function, they are only applied once. If you called foo_no_args()
more than once, the decorator_no_args
message will not be repeated.
Upvotes: 1
Reputation: 6124
If I recall, decorators are essentially functions that take a function as an argument and return another function (possibly the same). That's funception.
The two first lines of your output (the decorator_*
ones) come from the functions foo_no_args
and foo_args
being defined, not called.
To better understand, have a look at this snippet:
def my_decorator(func):
def wrap():
print "Function ran"
return func()
print "Function defined"
return wrap
@my_decorator
def my_function():
print "my_function"
my_function()
my_function()
my_function()
Outputs:
Function defined
function ran
my_function
function ran
my_function
function ran
my_function
Upvotes: 0