Python decorators with examples

Posted on Tue 18 Apr 2023

Subscribe to Web Distro Newsletter
and never miss any latest articles.

Python decorators are a powerful tool for extending the functionality of existing code. They are essentially functions that modify the behavior of other functions or classes. In this blog post, we will explore what decorators are, how to use them, and provide some examples of how decorators can be used to add additional functionality to Python code.

What are Decorators?

In Python, a decorator is a special type of function that takes another function as its argument and returns a new function that incorporates the behavior of the original function. In other words, decorators allow you to modify the behavior of a function without changing its code.

Here's a simple example of a decorator function:

def my_decorator(func):
    def wrapper():
        print("Before function is called.")
        func()
        print("After function is called.")
    return wrapper

 

In this example, we define a decorator function called my_decorator that takes another function as its argument (func). Inside the decorator function, we define a new function called wrapper that prints a message before and after calling the original function. Finally, the decorator function returns the wrapper function.

To use the decorator, we simply apply it to another function using the @ symbol:

>>> my_function()
Before function is called.
Hello, world!
After function is called.

 

This is just a simple example of how decorators work. In practice, decorators can be much more complex and can be used to add a wide variety of functionality to Python code.

Examples of Decorators in Python

Let's explore some more examples of how decorators can be used to add additional functionality to Python code.

  1. Timing Decorator

One common use of decorators is to add timing functionality to functions. Here's an example of a timing decorator:

import time

def time_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print("Function took", end_time - start_time, "seconds to complete.")
        return result
    return wrapper

 

In this example, we define a decorator function called time_decorator that takes another function as its argument. Inside the decorator function, we define a new function called wrapper that calculates the start and end times, calls the original function, calculates the total time taken, and prints the result.

To use the timing decorator, we simply apply it to another function using the @ symbol:

@time_decorator
def my_function():
    time.sleep(1)

 

In this example, we apply the time_decorator function to the my_function function using the @ symbol. Now, when we call my_function, the timing decorator will be called first, and it will print the time taken to execute the function:

>>> my_function()
Function took 1.0011999607086182 seconds to complete.

 

  1. Authorization Decorator

Another common use of decorators is to add authorization functionality to functions. Here's an example of an authorization decorator:

def authorized(allowed_roles):
    def decorator(func):
        def wrapper(*args, **kwargs):
            user_roles = get_user_roles()

In the example, we define a decorator function called authorized that takes a list of allowed roles as its argument. Inside the decorator function, we define a new function called decorator that takes another function as its argument. Inside the decorator function, we define a new function called wrapper that checks if the user has the appropriate roles to call the original function. If the user has the appropriate roles, the original function is called. If the user does not have the appropriate roles, an error message is returned.

To use the authorization decorator, we simply apply it to another function using the @ symbol:

@authorized(allowed_roles=["admin", "moderator"])
def my_function():
    print("Hello, world!")

 

In this example, we apply the authorized function to the my_function function using the @ symbol. Now, when we call my_function, the authorization decorator will be called first, and it will check if the user has the appropriate roles:

>>> my_function()
Error: User does not have permission to call this function.

In this example, the get_user_roles() function would need to be defined somewhere in the code to determine the user's roles.

    

    3. Memoization

Decorators can be used to cache the results of a function so that it doesn't have to be recomputed every time it is called. This is known as memoization. Here's an example:

def memoize(func):
    cache = {}
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def fibonacci(n):
    if n in (0, 1):
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

 

In this example, the memoize decorator caches the results of the fibonacci function. The wrapper function inside the decorator checks if the arguments to the function have been cached already. If they have, it returns the cached result. If they haven't, it calls the original function and caches the result.

Conclusion

Python decorators are a powerful tool for extending the functionality of existing code. They allow you to modify the behavior of a function without changing its code. In this blog post, we explored what decorators are, how to use them, and provided some examples of how decorators can be used to add additional functionality to Python code. While decorators can be complex, they are a valuable tool to have in your programming arsenal.

 

 

Check articles with similar categories