Rafa / 菲娜 | 小王子
897 字
4 分钟
【Python】Python中的修饰器
Python 装饰器(Decorator)详解
什么是装饰器?
装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个新的函数。它可以在不修改原函数代码的情况下,增强或改变函数的行为。
# 装饰器本质@decoratordef func(): pass
# 等价于decorator(func)最简单的装饰器示例
def my_decorator(func): def wrapper(): print("函数执行前...") func() print("函数执行后...") return wrapper
@my_decoratordef say_hello(): print("Hello!")
say_hello()输出:
函数执行前...Hello!函数执行后...装饰器的执行流程
┌─────────────────────────────────────────────────────────┐│ @my_decorator ││ def say_hello(): ││ print("Hello!") │└─────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────┐│ 1. Python 解释器看到 @my_decorator ││ 2. 调用 my_decorator(say_hello) ││ 3. 返回 wrapper 函数 ││ 4. say_hello = wrapper │└─────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────┐│ 调用 say_hello() 实际上是调用 wrapper() │└─────────────────────────────────────────────────────────┘使用场景
1. 计时器 - 测量函数执行时间
import time
def timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} 执行耗时: {end - start:.4f} 秒") return result return wrapper
@timerdef slow_function(): time.sleep(1) print("完成!")
slow_function()# 输出:# 完成!# slow_function 执行耗时: 1.0012 秒2. 日志记录
def log(func): def wrapper(*args, **kwargs): print(f"调用函数: {func.__name__}, 参数: {args}, {kwargs}") result = func(*args, **kwargs) print(f"函数返回: {result}") return result return wrapper
@logdef add(a, b): return a + b
add(3, 5)# 输出:# 调用函数: add, 参数: (3, 5), {}# 函数返回: 83. 权限验证
def require_login(func): def wrapper(user, *args, **kwargs): if not user.get('is_logged_in'): print("请先登录!") return None return func(user, *args, **kwargs) return wrapper
@require_logindef view_profile(user): print(f"欢迎, {user['name']}!")
view_profile({'name': 'Alice', 'is_logged_in': False}) # 请先登录!view_profile({'name': 'Alice', 'is_logged_in': True}) # 欢迎, Alice!4. 缓存(记忆化)
def cache(func): cached_results = {} def wrapper(*args): if args in cached_results: print(f"从缓存获取: {args}") return cached_results[args] result = func(*args) cached_results[args] = result return result return wrapper
@cachedef fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 第一次计算print(fibonacci(10)) # 从缓存获取5. 重试机制
import random
def retry(max_attempts=3): def decorator(func): def wrapper(*args, **kwargs): for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: print(f"第 {attempt + 1} 次尝试失败: {e}") print("所有尝试均失败!") return wrapper return decorator
@retry(max_attempts=3)def unstable_api(): if random.random() < 0.7: raise ConnectionError("网络错误") return "成功!"
unstable_api()带参数的装饰器
def repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return decorator
@repeat(times=3)def greet(name): print(f"Hello, {name}!")
greet("World")# 输出:# Hello, World!# Hello, World!# Hello, World!结构对比:
普通装饰器(2层) 带参数的装饰器(3层)───────────────── ─────────────────────def decorator(func): def decorator_factory(arg): def wrapper(): def decorator(func): ... def wrapper(): return wrapper ... return wrapper return decorator类装饰器
装饰器也可以是一个类:
class CountCalls: def __init__(self, func): self.func = func self.count = 0
def __call__(self, *args, **kwargs): self.count += 1 print(f"{self.func.__name__} 被调用了 {self.count} 次") return self.func(*args, **kwargs)
@CountCallsdef say_hi(): print("Hi!")
say_hi() # say_hi 被调用了 1 次 \n Hi!say_hi() # say_hi 被调用了 2 次 \n Hi!多个装饰器叠加
@decorator1@decorator2@decorator3def func(): pass
# 等价于func = decorator1(decorator2(decorator3(func)))执行顺序: 从下往上装饰,从上往下执行
保留原函数信息
使用 functools.wraps 保留原函数的元信息:
from functools import wraps
def my_decorator(func): @wraps(func) # 保留原函数的 __name__, __doc__ 等 def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
@my_decoratordef example(): """这是文档字符串""" pass
print(example.__name__) # 输出: example(而不是 wrapper)print(example.__doc__) # 输出: 这是文档字符串总结
| 使用场景 | 示例 |
|---|---|
| 日志/调试 | 记录函数调用信息 |
| 性能监控 | 计时、计数 |
| 权限控制 | 登录验证、角色检查 |
| 缓存 | 避免重复计算 |
| 重试机制 | 网络请求失败重试 |
| 输入验证 | 参数类型检查 |
| 注册机制 | Flask 路由、d2l 的 add_to_class |
装饰器是 Python 中实现 AOP(面向切面编程) 的优雅方式,让你能够将横切关注点(如日志、缓存、权限)与业务逻辑分离!
【Python】Python中的修饰器
http://onemom.top/posts/python_decorater/