Python decorators with parameters

Python decorators with parameters

In the previous articles on the Python decorator series, we have learnt decorators, how they work and to implement a simple function based decorator and a class based decorator. In this article we will learn to create decorators that supports parameters.

Function based decorator with parameters

from functools import wraps


def hello_decorator(num):
    """Simple decorator function that supports parameters"""

    def inner_func(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            """Simple decorator wrapper function"""
            result = func(*args, **kwargs)
            result = result + num
            return result
        return wrapper
    return inner_func


@hello_decorator(100)
def add(a, b):
    """Simple function that returns sum of two numbers"""
    return a + b


@hello_decorator(200)
def multiply(a, b):
    """Simple function that returns multiplication of two numbers"""
    return a * b


if __name__ == '__main__':
    output1 = add(2, 2)
    print('Result:: ', output1)
    print("=" * 25)

    output2 = multiply(4, 2)
    print('Result:: ', output2)

As you notice, the structure is little different from our previous examples,

  • decorator takes a parameter @hello_decorator(100) - this is how we can pass arguments to our decorator
  • hello_decorator function returns an inner function.
  • inner_func takes the function to be decorated as an argument and returns the wrapper function.
  • wrapper function executes the add function and manipulates the output based on argument result = result + num and returns the final result

Class based decorator with parameters

from functools import wraps


class HelloDecorator:
    """Simple class decorator"""

    def __init__(self, num):
        self.num = num

    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            """Simple class call method"""
            result = func(*args, **kwargs)
            result = result + self.num
            return result
        return wrapper


@HelloDecorator(100)
def add(a, b):
    """Simple function that returns sum of two numbers"""
    return a + b


@HelloDecorator(200)
def multiply(a, b):
    """Simple function that returns multiplication of two numbers"""
    return a * b


if __name__ == '__main__':
    output1 = add(2, 2)
    print('Result:: ', output1)

    output2 = multiply(4, 2)
    print('Result:: ', output2)

This class based decorator with parameters is pretty much similar to our simple function based decorator. The best thing with this method is, we do not need the extra boilerplate code to fix the doc strings.

So now to implement decorator with arguments, I would prefer class based approach. since it is very intuitive and doesn't requires an additional inner function and additional boilerplate code fixes for docs.

So far, we've covered the basic decorator implementation with examples. In the next article, we will implement various kinds decorator recipes. Stay tuned for upcoming articles. Subscribe to the newsletter and Connect with me on twitter to get my future articles.

Did you find this article valuable?

Support Suresh Kumar by becoming a sponsor. Any amount is appreciated!