「每日代码」2026-05-20 Python 装饰器学习报告
📅 今日主题:Python 装饰器
今天花了两小时深入理解 Python 装饰器。之前只会用 @app.route() 这种现成的,今天终于搞懂了它的底层原理。
✅ 核心收获
1. 装饰器就是高阶函数
本质上就是”接受函数、返回函数”。@decorator 只是语法糖,等价于 func = decorator(func)。理解了这一点,三层嵌套也不难了。
2. @wraps 不能省
不加 @wraps 会导致原函数的 __name__、__doc__ 被覆盖成 wrapper 的信息。调试时函数名全变成 wrapper,排查问题会很痛苦。
3. 带参数的装饰器 = 三层嵌套
第一层接收装饰器参数,第二层接收被装饰函数,第三层是实际 wrapper。今天写了 retry 装饰器才真正理解这个模式。
💻 今天写的代码
实现了一个带指数退避的重试装饰器:
import time
from functools import wraps
def retry(max_retries=3, base_delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if i == max_retries - 1: raise
wait = base_delay * (2 ** i)
print(f"[重试 {i+1}/{max_retries}] 等待 {wait}s: {e}")
time.sleep(wait)
return wrapper
return decorator
@retry(max_retries=3, base_delay=1)
def call_api(endpoint):
import random
if random.random() < 0.7:
raise ConnectionError("网络抖动")
return f"{endpoint} 调用成功"
这个在实际项目中立刻用上了——替换掉了之前手写的 20 行 try-except 重试逻辑。
⚠️ 踩坑记录
坑:装饰器内定义的 cache = {} 字典是所有调用共享的,不是线程安全的。
解决:改用 functools.lru_cache,它内部有锁保护。生产环境别手写缓存装饰器。
📌 明日计划
继续学 Python 高级特性——上下文管理器(with 语句)和 __enter__/__exit__。