Search code examples
pythondecoratorpython-decorators

Decorators with arguments


Code as follows

def my_dec(func):
    def wrap(w):
        t = func(w)
        return t * 4
    return wrap


@my_dec
def testing(n):
    return n


new = testing(3)
print(new)  # This prints 12

This example is working fine, but now I'm trying to add the following to the decorator @my_dec(100), I need to multiply the given number by 100.

When I try this

@my_dec(100)
def testing(n):
    return n

I get the following error:

Traceback (most recent call last):
  File "./deco2", line 10, in <module>
    @my_dec(100)
  File "./deco2", line 5, in wrap
    t = func(w)
TypeError: 'int' object is not callable

How can I pass the 100 to the decorator?


Solution

  • In the first example, you correctly define the decorator as a function which takes the wrapped function, and returns a new function.

    To add arguments, you need to write a function which takes the arguments, and returns the decorator, i.e. returns the function which takes the wrapped function and returns a function.

    One way to do it:

    def my_dec(x):
        def dec(func):
            def wrap(w):
                t = func(w)
                return t * x
            return wrap
        return dec
    

    This might be slightly clearer if you think about what the @ syntax expands to:

    @my_dec(100)
    def testing(n):
        return n
    

    expands to:

    def testing(n):
        return n
    testing = my_dec(100)(testing)
    

    Also, to reduce the high nesting level and make your decorator more readable and maintainable, you can define a decorator class instead, which takes its arguments in its __init__, and calls the wrapped function in its __call__. There are plenty of examples online. You can start by reading this question.

    There are also ways to make your decorator take optional arguments (i.e. so that both your examples work).


    And if you really want to understand decorators in depth, read Graham Dumpleton's blog. Highly recommended.