跳到主要内容

说明 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 均是装饰器