Python 24-Day Course - Day 15: Decorators

Day 15: Decorators

What Is a Decorator?

A pattern that wraps a function to insert additional behavior before and after execution.

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} execution time: {elapsed:.4f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "done"

slow_function()  # slow_function execution time: 1.00xxs

functools.wraps

Preserves the metadata of the original function.

from functools import wraps

def log_call(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Called: {func.__name__}({args}, {kwargs})")
        return func(*args, **kwargs)
    return wrapper

@log_call
def add(a, b):
    """Adds two numbers."""
    return a + b

print(add.__name__)  # add (would be wrapper without wraps)
print(add.__doc__)   # Adds two numbers.

Decorators with Parameters

def repeat(n):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
# Hello, Alice! (printed 3 times)

Practical Decorator: Cache

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # 12586269025 (computed quickly)

Today’s Exercises

  1. Create a count_calls decorator that counts how many times a function is called.
  2. Create a validate_positive decorator that raises an error if the return value is negative.
  3. Create a retry(max_attempts) decorator that automatically retries function execution.

Was this article helpful?