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.
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 * n7. 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).
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
