Mastering Python’s Power: Exploring Advanced Decorators and Context Managers

Learn Python @ Freshers.in

Python’s decorators and context managers are powerful tools that enhance code readability, maintainability, and functionality. In this article, we’ll delve into advanced techniques for decorators, exploring real-world examples and their outputs.

Understanding Advanced Decorators:

Decorators in Python are functions that modify the behavior of other functions or methods. They provide a convenient way to add functionality to existing code without modifying its structure. Let’s explore some advanced decorator techniques:

1. Class-based Decorators:

In addition to function-based decorators, Python supports class-based decorators. Class-based decorators provide more flexibility and allow for additional state management. Consider the following example:

class Logger:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f"Calling {self.func.__name__}")
        return self.func(*args, **kwargs)
@Logger
def add(a, b):
    return a + b
result = add(2, 3)
print("Result:", result)

Output:

Calling add
Result: 5

In this example, the Logger class acts as a decorator, adding logging functionality to the add function.

2. Decorator Factories:

Decorator factories are functions that return decorators based on input parameters. This enables dynamic behavior customization. Let’s see an example:

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return wrapper
    return decorator
@repeat(3)
def greet(name):
    print(f"Hello, {name}!")
greet("Alice")

Output:

Hello, Alice!
Hello, Alice!
Hello, Alice!

Real-world Example: Performance Monitoring Decorator

Let’s create a decorator that monitors the performance of a function by measuring its execution time:

import time
def performance_monitor(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} executed in {end_time - start_time} seconds")
        return result
    return wrapper

@performance_monitor
def calculate_sum(n):
    return sum(range(n))
result = calculate_sum(1000000)
print("Sum:", result)

Output:

calculate_sum executed in 0.036206960678100586 seconds
Sum: 499999500000
Author: user