Python Decorators: Function Wrappers, Closures & Metaprogramming

In Python, functions are first-class citizens. This means functions can be assigned to variables, passed as arguments into other functions, and returned from functions just like integers or strings. A decorator leverages this capability to wrap another function, extending or modifying its behavior without altering its underlying code.

Python Decorators Architecture and Wrapper Execution Flow

Figure 35.1: Decorator wrapping execution: pre-execution setup -> target function invocation -> post-execution cleanup.


1. Building Your First Decorator from Scratch

Let us write a decorator that logs when a function starts and finishes:

import functools

def log_execution(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[LOG] Executing function: '{func.__name__}'...")
        result = func(*args, **kwargs)
        print(f"[LOG] Completed '{func.__name__}' successfully!")
        return result
    return wrapper

# Applying the decorator using @ syntax:
@log_execution
def generate_bill(customer_name: str, total_amount: float):
    print(f"Generating invoice for {customer_name}: ₹{total_amount}")
    return True

# Call decorated function:
generate_bill("Deepak Kumar", 4500.0)

Output:

[LOG] Executing function: 'generate_bill'...
Generating invoice for Deepak Kumar: ₹4500.0
[LOG] Completed 'generate_bill' successfully!

2. Real-World Decorator: Execution Timing Benchmark

import time
import functools

def benchmark(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start_time
        print(f"[BENCHMARK] {func.__name__}() took {elapsed:.6f} seconds to run.")
        return result
    return wrapper

@benchmark
def calculate_large_sum():
    return sum(x ** 2 for x in range(1_000_000))

calculate_large_sum()

Frequently Asked Questions (FAQ)

Can you apply multiple decorators to a single function?

Yes! Multiple decorators can be stacked on a function. They are applied from the bottom up (closest to the function first) and executed in reverse order.
Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!