专栏进度:03 / 03 (装饰器专题收官)
在前两篇中,我们学会了如何给函数“整容”。但现实业务往往更复杂:

场景 A:你写了一个权限校验装饰器,但有的接口需要 admin 权限,有的只需要 user 权限。

场景 B:你写了一个重试装饰器,但不同的接口需要不同的重试次数。

这种情况下,普通的装饰器就不够用了,我们需要带参数的装饰器。

一、 三层嵌套:带参数装饰器的逻辑套娃

要让装饰器接收参数,我们需要在原有“两层嵌套”的基础上,再包一层工厂函数。

实战:一个可以指定“重试次数”的装饰器

from functools import wraps

def retry(times):
    """最外层:负责接收参数"""
    def decorator(func):
        """中间层:传统的装饰器"""
        @wraps(func)
        def wrapper(*args, **kwargs):
            """最内层:实际的逻辑劫持"""
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"尝试第 {i+1} 次失败,原因: {e}")
            return None
        return wrapper
    return decorator

@retry(times=3)
def fetch_data():
    raise ConnectionError("网络波动")

fetch_data()

理解口诀:

外层传参(times)。

中层传函数(func)。

内层传数据(*args, **kwargs)。

二、 类装饰器:用“对象”管理复杂的装饰逻辑

当你的装饰器逻辑变得非常庞大(例如需要记录状态、缓存数据)时,三层嵌套会让代码阅读者感到痛苦。这时,类装饰器是更优雅的选择。

原理:利用类的 init 接收函数,利用 call 实现劫持。

实战:简单的单例模式装饰器

class Singleton:
    def __init__(self, cls):
        self._cls = cls
        self._instance = None

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = self._cls(*args, **kwargs)
        return self._instance

@Singleton
class DatabaseConnection:
    def __init__(self):
        print("连接数据库...")

无论创建多少次,永远是同一个对象

db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True

提示:类装饰器非常适合用来做状态保持。比如统计某个接口在整个生命周期内被调用了多少次。

三、 避坑指南:带参数装饰器的“消失调用”

新手常犯的错误:

错误:@retry (漏掉了括号)

正确:@retry(3) 或 @retry()

如果你的装饰器设计为接收参数,那么即便你使用默认参数,也必须带上括号。因为 @retry() 返回的是真正的装饰器函数,而直接写 @retry 传递的是工厂函数本身。

四、 专题总结:逻辑劫持的架构哲学

从闭包的内存胶囊,到装饰器的优雅整容,再到带参/类装饰器的黑科技,我们完成了 Python 函数式编程的高级跨越。

核心心法:

解耦:将非业务逻辑(日志、性能、权限)抽离。

复用:一行 @ 代码实现千行代码的注入。

克制:不要为了用装饰器而写装饰器,保持代码的直观性。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐