kuzzooroo
kuzzooroo

Reputation: 7408

Why are Python decorators with arguments syntactically different from those without?

This article, linked to a number of times from various stackoverflow questions, describes how decorators with arguments are syntactically different from those without arguments.

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:

  1. Why provide a way to make no-argument decorators that's not just a special case of the more general method for specifying decorators? And why use __init__ and __call__ in both, but have them mean different things?
  2. Why use __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?

Upvotes: 8

Views: 1438

Answers (1)

Benno
Benno

Reputation: 5640

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.

Upvotes: 9

Related Questions