This article, linked to a number of times from various stackoverflow questions, describes how decorators with arguments are syntactically different from those without arguments.
__init__()
is the only method called to perform decoration, and __call__()
is called every time you call the decorated sayHello()
."__call__()
, which can only take a single argument (the function object) and must return the decorated function object that replaces the original. Notice that __call__()
is now only invoked once, during decoration, and after that the decorated function that you return from __call__()
is used for the actual calls."The explanation given in the article doesn't tell me why the language is set up this way:
Although this behavior makes sense -- the constructor is now used to capture the decorator arguments, but the object
__call__()
can no longer be used as the decorated function call, so you must instead use__call__()
to perform the decoration -- it is nonetheless surprising the first time you see it
There are two related features of this setup that are uncomfortable for me:
__init__
and __call__
in both, but have them mean different things?__call__
in the case of decorators with arguments for a purpose other than calling the decorated function (as the name suggests, at least coming from the no argument case)? Given that __call__
is only invoked right after __init__
, why not just pass the function to be decorated as an argument to __init__
and handle everything that would happen in __call__
in __init__
instead?It's because it's the decorator object that's being called in both cases. To make it clearer, given this:
def my_decorator(a):
def wrapper(f):
def wrapped_function():
return f() + a
return wrapped_function
return wrapper
this:
@my_decorator(5)
def function():
return 5
is equivalent to this:
decorator = my_decorator(5)
@decorator
def function():
return 5
What's happening in the no-argument case is that the decorator gets invoked directly instead of having to return a function that takes the object to be decorated as a parameter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With