Arif Khan
A Little Coding

Follow

A Little Coding

Follow
Python decorators simplified

Python decorators simplified

Arif Khan's photo
Arif Khan
·Apr 4, 2023·

2 min read

Put simply: Decorators wrap a function, modifying its behavior.

A simple decorator example:

def my_decorator(function):
    def wrapper():
        print("Something is happening before the function is called.")
        function()
        # do other stuff
        print("Something is happening after the function is called.")
    return wrapper

In this example, this decorator is changing the function's behavior by doing extra stuff before and after the function call.

There are mainly two ways we can use the above decorator.

Method 1:

def say_whee():
    print("Whee!")

my_decorator(say_whee)

Method 2:

@my_decorator
def say_whee():
    print("Whee!")

We will use the 2nd method in upcoming examples as it’s more readable.

Example use:

  1. we can use a decorator to add logs before and after the execution of functions.

  2. we can use a decorator to calculate and print the execution time of functions.

Decorator for function with return value:

def my_decorator(function):
    def wrapper():
        print("Something is happening before the function is called.")
        value = function()
        # do other stuff
        print("Something is happening after the function is called.", value)
        return value
    return wrapper

Notice that we have just added the return value we got from calling the function.

Example use:

@my_decorator
def say_whee():
    print("Whee!")
    return 100

Decorator for function with arguments:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
@my_decorator
def say_whee(name):
    print("Hello, " + name)
    return 100

Passing arguments to the decorator:

def exception_handler(except_returns=None):  # for multiple values pass in tuple e.g (value1, value2)
    def decorator(function):
        @wraps(function)
        def wrapper(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except Exception as e:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                message = f"Exception in {function.__name__}, {exc_value}"
                print(message)
                return except_returns
        return wrapper
    return decorator

Notice that we are accepting values in the except_returns argument. The idea is to use this value when an exception occurs.

Technical Detail: The @functools.wraps decorator uses the function functools.update_wrapper()
to update special attributes like __name__ and __doc__ that are used in the introspection.

Use:

@exception_handler(0)
def function_with_exception(value, val2, val3):
    print(a) # a is not defined
    return 100

value = function_with_exception(1, 2, 3)  # this will return 0

This article is a simplified version of these sources:

For ref: https://realpython.com/primer-on-python-decorators/#decorators-with-arguments

Class decorators: https://stackoverflow.com/a/9647491

decorators with args: https://stackoverflow.com/questions/5929107/decorators-with-parameters

Did you find this article valuable?

Support Arif Khan by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this