装饰器深度解析
装饰器是 Python 最优雅的特性之一,也是框架开发的核心工具。从语法糖到底层原理,彻底掌握它。
装饰器本质
装饰器就是接受函数/类作为参数,返回新函数/类的可调用对象。
python
# 这两种写法完全等价
@decorator
def func():
pass
# 等价于:
def func():
pass
func = decorator(func)函数装饰器
最简单的装饰器
python
import functools
def timer(func):
@functools.wraps(func) # 保留原函数元信息
def wrapper(*args, **kwargs):
import time
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 耗时 {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
import time
time.sleep(0.1)
slow_function() # slow_function 耗时 0.1001sfunctools.wraps 的重要性
python
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def good_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@bad_decorator
def my_func():
"""我的函数文档"""
pass
@good_decorator
def my_func2():
"""我的函数文档"""
pass
print(my_func.__name__) # wrapper — 元信息丢失!
print(my_func2.__name__) # my_func2 — 正确保留
print(my_func2.__doc__) # 我的函数文档带参数的装饰器
需要多一层嵌套:
python
def retry(max_times=3, exceptions=(Exception,)):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_times):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_times - 1:
raise
print(f"第 {attempt+1} 次失败: {e},重试中...")
return wrapper
return decorator
@retry(max_times=3, exceptions=(ConnectionError,))
def fetch_data(url):
# 模拟网络请求
raise ConnectionError("网络超时")
# fetch_data("http://example.com")类装饰器
用类实现装饰器(有状态)
python
class CallCounter:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"第 {self.count} 次调用 {self.func.__name__}")
return self.func(*args, **kwargs)
@CallCounter
def greet(name):
return f"Hello, {name}!"
greet("Alice") # 第 1 次调用 greet
greet("Bob") # 第 2 次调用 greet
print(greet.count) # 2装饰类
python
def singleton(cls):
"""单例模式装饰器"""
instances = {}
@functools.wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class DatabaseConnection:
def __init__(self):
print("创建数据库连接")
db1 = DatabaseConnection() # 创建数据库连接
db2 = DatabaseConnection() # 不再打印
print(db1 is db2) # True实用装饰器库
functools.lru_cache — 缓存
python
from functools import lru_cache, cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(100)) # 极快,无递归爆栈
print(fibonacci.cache_info()) # CacheInfo(hits=98, misses=101, ...)
# Python 3.9+ 无限缓存
@cache
def factorial(n):
return n * factorial(n-1) if n else 1functools.cached_property — 惰性属性
python
class Circle:
def __init__(self, radius):
self.radius = radius
@functools.cached_property
def area(self):
import math
print("计算面积...")
return math.pi * self.radius ** 2
c = Circle(5)
print(c.area) # 计算面积... 78.53...
print(c.area) # 直接返回缓存,不再计算装饰器叠加顺序
python
def decorator_a(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("A 前")
result = func(*args, **kwargs)
print("A 后")
return result
return wrapper
def decorator_b(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("B 前")
result = func(*args, **kwargs)
print("B 后")
return result
return wrapper
@decorator_a
@decorator_b
def hello():
print("Hello!")
hello()
# 输出:
# A 前
# B 前
# Hello!
# B 后
# A 后叠加顺序:从下到上应用,从外到内执行。@a @b func 等价于 a(b(func))。
实战:权限控制装饰器
python
from functools import wraps
def require_permission(*permissions):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
user_perms = getattr(user, 'permissions', set())
missing = set(permissions) - user_perms
if missing:
raise PermissionError(f"缺少权限: {missing}")
return func(user, *args, **kwargs)
return wrapper
return decorator
class User:
def __init__(self, name, permissions):
self.name = name
self.permissions = set(permissions)
@require_permission('admin', 'write')
def delete_user(current_user, target_id):
print(f"{current_user.name} 删除用户 {target_id}")
admin = User("Alice", ['admin', 'write', 'read'])
guest = User("Bob", ['read'])
delete_user(admin, 42) # Alice 删除用户 42
# delete_user(guest, 42) # PermissionError: 缺少权限: {'admin', 'write'}实战:类型检查装饰器
python
import inspect
from functools import wraps
def type_check(func):
hints = func.__annotations__
sig = inspect.signature(func)
@wraps(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
for param_name, value in bound.arguments.items():
if param_name in hints:
expected = hints[param_name]
if not isinstance(value, expected):
raise TypeError(
f"参数 '{param_name}' 期望 {expected.__name__},"
f"得到 {type(value).__name__}"
)
return func(*args, **kwargs)
return wrapper
@type_check
def add(a: int, b: int) -> int:
return a + b
print(add(1, 2)) # 3
# add(1, "2") # TypeError: 参数 'b' 期望 int,得到 str框架中的装饰器
FastAPI 的 @app.get()、Flask 的 @app.route()、pytest 的 @pytest.fixture 都是装饰器的典型应用。理解装饰器原理,读框架源码就不再神秘。