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