Introduction

Python decorators are a powerful feature that allows you to modify the behavior of functions or classes dynamically. Decorators provide a way to add functionality to existing code without modifying the original source. This blog post will delve into the concept of decorators in Python, starting from the basics and gradually progressing to more advanced techniques.

Understanding Decorators

Function Decorators

Function decorators are a way to modify the behavior of a function by wrapping it inside another function. The decorator function takes the original function as an argument, adds some functionality, and returns a modified function. This allows you to enhance or extend the behavior of functions without modifying their source code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def uppercase_decorator(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

@uppercase_decorator
def say_hello():
    return "Hello, World!"

print(say_hello())  # Output: HELLO, WORLD!

In the example above, the uppercase_decorator function is defined to wrap the say_hello function. It modifies the behavior by converting the returned string to uppercase. The @uppercase_decorator syntax is used to apply the decorator to the say_hello function.

Class Decorators

Class decorators are similar to function decorators but operate on classes instead of functions. They allow you to modify the behavior or add functionality to a class. The decorator function takes the original class as an argument, creates a derived class with added functionality, and returns the modified class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def add_method_decorator(cls):
    def new_method(self):
        return "New method added!"
    cls.new_method = new_method
    return cls

@add_method_decorator
class MyClass:
    def existing_method(self):
        return "Existing method called!"

obj = MyClass()
print(obj.existing_method())  # Output: Existing method called!
print(obj.new_method())  # Output: New method added!

In the example above, the add_method_decorator function wraps the MyClass class and adds a new method called new_method. The @add_method_decorator syntax is used to apply the decorator to the MyClass class.

Decorator Syntax and Execution

When using decorators, it’s important to understand the order of execution. Decorators are applied from the bottom up, meaning the decorator defined at the top is executed last. This order is crucial when multiple decorators are applied to the same function or class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def decorator1(func):
    print("Decorator 1 executed")
    return func

def decorator2(func):
    print("Decorator 2 executed")
    return func

@decorator1
@decorator2
def my_function():
    print("Inside my_function")

my_function()

Output:

1
2
3
Decorator 2 executed
Decorator 1 executed
Inside my_function

In the example above, the decorator2 decorator is executed first, followed by the decorator1 decorator. The my_function is then called, and the output reflects the order of execution.