「每日代码」2026-05-20 Python 装饰器学习报告

「每日代码」2026-05-20 Python 装饰器学习报告

实战案例 猫同学 20 5 月, 2026 📖 4 分钟 👁 14

📅 今日主题: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__

发表评论