装饰器
基本概念
装饰器是接收函数并返回新函数的高阶函数,通过 @ 语法糖应用。
def my_decorator(func):
def wrapper(*args, **kwargs):
print("调用前")
result = func(*args, **kwargs)
print("调用后")
return result
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# 调用前
# Hello, Alice!
# 调用后functools.wraps
不使用 wraps 会丢失原函数的 __name__、__doc__ 等元信息。
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数元信息
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def add(a, b):
"""返回两数之和"""
return a + b
add.__name__ # "add"(不加 wraps 则为 "wrapper")
add.__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 hello():
print("Hello!")
hello() # 打印 3 次 "Hello!"基于类的装饰器
通过实现 __call__ 方法创建可调用对象作为装饰器。
from functools import wraps
class Retry:
def __init__(self, times=3):
self.times = times
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(self.times):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == self.times - 1:
raise
print(f"第 {attempt+1} 次失败,重试...")
return wrapper
@Retry(times=3)
def fetch_data():
...实用装饰器示例
计时器
import time
from functools import wraps
def timeit(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 耗时 {elapsed:.4f}s")
return result
return wrapper
@timeit
def slow_fn():
time.sleep(0.1)缓存(手动实现)
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)登录验证
from functools import wraps
def login_required(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated:
raise PermissionError("需要登录")
return func(request, *args, **kwargs)
return wrapper堆叠装饰器
多个装饰器从下往上应用(最靠近函数的先执行)。
@decorator_a
@decorator_b
@decorator_c
def fn():
pass
# 等价于
fn = decorator_a(decorator_b(decorator_c(fn)))内置装饰器速查
| 装饰器 | 用途 |
|---|---|
@property | 属性访问器 |
@classmethod | 类方法,第一参数为 cls |
@staticmethod | 静态方法,无隐式参数 |
@functools.lru_cache | 自动 LRU 缓存 |
@functools.cached_property | 延迟计算且缓存的属性(3.8+) |
@dataclasses.dataclass | 自动生成 __init__ 等方法 |
@abstractmethod | 声明抽象方法 |