Search code examples
pythonpython-decorators

What does it mean to decorate with an argument in Python?


In Python I know a little about decorators. In my poor understanding a decorated funtion declaration returns whatever the decorator function constructs with the original function (most reasonably this should be a callable):

def deco(func):
    def func_wrapper(name):
        return func(name)+", how are you?"
    return func_wrapper

@deco
def foo(name):
    return "Hello "+name

print(foo("Michael"))

This gives:

Hello Michael, how are you?

But what if a decorator is specified along with an argument:

def deco2(info):
    def info_decorator(func):
        def func_wrapper(name):
            return func(name) + info
        return func_wrapper
    return info_decorator

@deco2(", how are you?")
def foo2(name):
    return "Hello "+name

print(foo2("Regina"))

giving

Hello Regina, how are you?

What is the "rule" for handling this decorator? It seems, that instead of returning foo2 Python first calls deco2 with the given arg, and assumes, that this call first constructs a factory for a decorator, which then acts like before with foo2 passed to it in order to return the final wrapper. However, in this case there is one additional "layer" involved (three defs instead of two), which makes it hard for me, to identify the general rule.


Solution

  • If you apply the decorator without arguments, e.g. @foo, then the decorator takes the function being decorated as its argument and is expected to return any callable that will replace the decorated function.

    If you apply the decorator with arguments, e.g. @foo('bar'), then the decorator is expected to return a callable that will take the decorated function as its argument and return and callable that will replace the decorated function.

    Put another way: a decorator without arguments only needs the function being decorated, but a decorator with arguments needs its own arguments and it needs to get a hold of the function being decorated somehow. Since the arguments to the decorator are arbitrary and not restricted in any way, it's hard to come up with any non-restrictive rules on how to additionally pass the decoratee to it as well. In that case the two-step process is applied: get decorator arguments, then return another callable that takes the decoratee.