Fundamentals 11 min read

Master Python Decorators: From Basics to Advanced Patterns

This comprehensive guide walks you through Python decorators, covering basic function decorators, parameterized decorators, class-based decorators, decorator factories, real-world applications such as routing, permission checks, and caching, and concludes with best practices, pitfalls, and a roadmap for deeper learning.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Master Python Decorators: From Basics to Advanced Patterns

1. Review: Basic Decorator

We start by revisiting the structure of a simple decorator that wraps a function, prints messages before and after the call, and returns the result.

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"准备调用函数: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"函数调用完成: {func.__name__}")
        return result
    return wrapper

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

greet("Python进阶者")

2. Parameterized Decorator

A decorator that accepts its own arguments needs a three‑level nesting: the outer factory, the actual decorator, and the wrapper.

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

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

say_hello("Python进阶者")

3. Class‑Based Decorator

Using a class allows state to be stored in the instance. The class implements __init__ to receive the function and __call__ to act as the wrapper.

class TimerDecorator:
    """记录函数执行时间的类装饰器"""
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        import time
        start = time.perf_counter()
        result = self.func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{self.func.__name__} 执行时间: {end - start:.4f}秒")
        return result

@TimerDecorator
def calculate_sum(n):
    return sum(range(n))

calculate_sum(1000000)

4. Stacking Multiple Decorators

When several decorators are applied, they are executed from the bottom up (the nearest to the function runs first).

@decorator1
@decorator2
@decorator3
def my_function():
    pass
# Equivalent to: my_function = decorator1(decorator2(decorator3(my_function)))

5. Decorator Factory for Dynamic Tags

A factory can generate decorators that wrap a function's return value in a specific HTML tag.

def tag_decorator_factory(tag_name):
    def decorator(func):
        def wrapper(*args, **kwargs):
            content = func(*args, **kwargs)
            return f"<{tag_name}>{content}</{tag_name}>"
        return wrapper
    return decorator

div = tag_decorator_factory("div")
@div
def greeting(name):
    return f"Hello, {name}!"

print(greeting("Python进阶者"))  # <div>Hello, Python进阶者!</div>

6. Real‑World Applications

6.1 Web Routing Decorator (Flask‑style)

routes = {}

def route(path):
    def decorator(func):
        routes[path] = func
        return func
    return decorator

@route("/")
def home():
    return "首页内容"

@route("/about")
def about():
    return "关于我们"

def handle_request(path):
    return routes.get(path, lambda: "404 页面不存在")()

6.2 Permission Check Decorator

from functools import wraps

def requires_role(role):
    def decorator(func):
        @wraps(func)
        def wrapper(user, *args, **kwargs):
            if user.get("role") != role:
                raise PermissionError(f"需要 {role} 权限")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

@requires_role("admin")
def delete_user(user, username):
    return f"用户 {username} 已被删除"

6.3 Caching Decorator with Size Limit

from functools import wraps

def cache(max_size=100):
    def decorator(func):
        cache_dict = {}
        @wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, frozenset(kwargs.items()))
            if key not in cache_dict:
                if len(cache_dict) >= max_size:
                    cache_dict.pop(next(iter(cache_dict)))
                cache_dict[key] = func(*args, **kwargs)
            return cache_dict[key]
        return wrapper
    return decorator

@cache(max_size=3)
def expensive_computation(n):
    print(f"执行计算: {n}")
    return n * n

7. Pitfalls & Best Practices

7.1 Preserve Function Metadata

Always use functools.wraps inside the wrapper to keep the original function's name, docstring, and other attributes.

7.2 Avoid Side Effects at Import Time

Do not place business logic directly in the decorator factory; keep it inside the wrapper so it runs only when the decorated function is called.

7.3 Debugging Decorators

Use the inspect module to print the call stack and arguments when troubleshooting complex decorator chains.

8. Summary & Learning Path

The article concludes with a roadmap:

Basic function decorators – understand functions as first‑class objects.

Parameterized decorators – master three‑level nesting.

Class decorators – learn __init__ and __call__ methods.

Decorator factories – generate families of related decorators.

Metaclasses & advanced metaprogramming – next step for deep Python mastery.

A decision table helps choose the right implementation based on the scenario (simple enhancement, need for parameters, stateful behavior, or generating families).

tutorialcodeAdvanced
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.