说明 Python3 中装饰器的用法
什么是装饰器
装饰器(Decorator)是一种包裹函数的语法糖,在不修改原函数代码的情况下,为其添加额外功能。本质是一个接收函数并返回新函数的高阶函数。
@decorator
def func():
...
# 等价于
func = decorator(func)
基础装饰器
def logger(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} 执行完毕")
return result
return wrapper
@logger
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# 调用 greet
# Hello, Alice!
# greet 执行完毕
保留原函数元信息:functools.wraps
不加 wraps 时,装饰后的函数会丢失原函数的 __name__、__doc__ 等信息:
from functools import wraps
def logger(func):
@wraps(func) # ← 保留原函数元信息
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
@logger
def greet(name):
"""打招呼函数"""
print(f"Hello, {name}!")
print(greet.__name__) # greet(而非 wrapper)
print(greet.__doc__) # 打招呼函数
带参数的装饰器
需要多嵌套一层函数来接收装饰器参数:
from functools import wraps
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 say_hi():
print("Hi!")
say_hi()
# Hi!
# Hi!
# Hi!
类装饰器
用类实现装饰器,通过 __call__ 方法使实例可调用:
from functools import wraps
class Timer:
def __init__(self, func):
wraps(func)(self)
self.func = func
def __call__(self, *args, **kwargs):
import time
start = time.perf_counter()
result = self.func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{self.func.__name__} 耗时 {elapsed:.4f}s")
return result
@Timer
def slow_add(a, b):
import time
time.sleep(0.1)
return a + b
slow_add(1, 2) # slow_add 耗时 0.1002s
叠加多个装饰器
装饰器从下往上依次包裹,从外往里依次执行:
def decorator_A(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("A 前")
result = func(*args, **kwargs)
print("A 后")
return result
return wrapper
def decorator_B(func):
@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 后
等价于 hello = decorator_A(decorator_B(hello))。
常见实战场景
计时器
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} 耗时 {time.perf_counter() - start:.4f}s")
return result
return wrapper
缓存(Memoize)
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)) # 极快,结果已缓存
权限校验
from functools import wraps
def require_auth(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if not user.get("is_admin"):
raise PermissionError("无权限")
return func(user, *args, **kwargs)
return wrapper
@require_auth
def delete_user(user, user_id):
print(f"删除用户 {user_id}")
重试机制
from functools import wraps
import time
def retry(times=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, times + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第 {attempt} 次失败: {e}")
if attempt < times:
time.sleep(delay)
raise RuntimeError(f"{func.__name__} 重试 {times} 次后仍失败")
return wrapper
return decorator
@retry(times=3, delay=0.5)
def fetch_data():
raise ConnectionError("网络错误")
总结
| 场景 | 写法 |
|---|---|
| 基础装饰器 | def decorator(func): ... |
| 保留元信息 | @wraps(func) |
| 带参数装饰器 | 三层嵌套函数 |
| 类装饰器 | 实现 __call__ |
| 多个装饰器叠加 | 从下往上包裹,从外往里执行 |
- 装饰器本质是闭包 + 高阶函数
- 始终使用
@functools.wraps保留原函数元信息 - Python 标准库中
@property、@staticmethod、@classmethod、@lru_cache均是装饰器