首页 新闻 会员 周边

看懂类装饰器3

0
[已关闭问题] 关闭于 2026-06-25 06:24

 

让我们严格按照 Python 的执行顺序,一步步拆解这段代码的运行流程:
阶段一:类定义时的“偷梁换柱”(执行 @Decorator(arg2=5, arg3=5) )
当 Python 解释器读到 class cls: 内部时,它会处理装饰器。
第一步:实例化装饰器对象
因为写了括号 () ,Python 首先执行 Decorator(arg2=5, arg3=5) 。
● 调用: Decorator.init(self, func=None, arg2=5, arg3=5)
● 判断: 此时没有传入函数,所以 func is None 成立。
● 关键操作(第 11 行):
● 代码执行了 self.func = partial(Decorator, arg1=1, arg2=2, arg3=3)
● 注意: 这里虽然传入了 arg2=5 ,但代码里写死的是 arg2=2 (这可能是个 Bug 或者演示陷阱,我们暂且按代码字面意思理解)。
● 结果: 创建了一个临时对象(我们叫它 temp_obj )。此时 temp_obj.func 变成了一个 “制造新 Decorator 对象的工厂函数”。

第二步:绑定目标函数
现在 Python 拿到了 temp_obj ,它要把下面的 method 函数传给它。
● 调用: temp_obj(method)
● 触发: 因为 temp_obj 是个对象,这会触发它的 call 方法。
● 执行 call :
● 代码执行 return self.func(*args, **kwargs) 。
● 这里的 self.func 就是刚才那个 partial(...) 。
● 相当于执行了 partial(...) (method) 。
● 结果: partial 被调用,生成了一个全新的、真正的 Decorator 对象(我们叫它 final_decorator )。
● 在这个新对象里, func 被赋值为 method 。
● 这个 final_decorator 最终被赋值给 cls.method 。

总结阶段一:
 cls.method 现在是一个 Decorator 实例,它的内部属性 self.func 指向原本的 method 函数。

阶段二:实例化与访问(执行 c = cls() 和 c.method )
第三步:获取方法描述符
当你执行 c.method 时,Python 发现 cls.method 是一个实现了 get 的对象(描述符)。
● 调用: final_decorator.get(instance=c, owner=cls)
● 判断: instance 是 c (不为 None)。
● 执行(第 24 行):
● return partial(self.call, instance)
● 这里把实例 c “粘”到了 call 上。
● 结果: 返回了一个新的偏函数对象,我们叫它 bound_method 。



阶段三:最终调用(执行 print(...(1119, 66)) )
第四步:执行业务逻辑
你拿着 bound_method 去调用 (1119, 66) 。
● 调用: bound_method(1119, 66)
● 触发: 这实际上是在调用 final_decorator.call(c, 1119, 66) 。
● 执行 call (第 15 行):
● return self.func(*args, **kwargs)
● 这里的 self.func 是最初的那个 method 函数。
● args 现在是 (c, 1119, 66) 。
● 最终执行: method(c, 1119, 66) -> return 1119 + 66 -> 1185。

问题补充:

partial  并不是简单地把两组参数像串糖葫芦一样串起来,而是调用时的遵循 “位置参数追加,关键字参数覆盖/合并” 的原则。

假设 partial_obj = partial(Decorator, a=1) 。
当你调用 partial_obj(b, c, a=99) 时,Python 的执行逻辑如下:
1. 处理位置参数:
● 原始绑定的位置参数(空) + 新传入的位置参数 (b, c)
● 结果: args = (b, c)
2. 处理关键字参数:
● 原始绑定的关键字 {'a': 1}
● 新传入的关键字 {'a': 99}
● 冲突解决:新传入的通常会覆盖旧的(或者在某些版本中报错,取决于具体实现,但在标准库 functools 中,调用时传入的同名关键字参数会覆盖 partial 预设的值)。
● 结果: kwargs = {'a': 99}
3. 最终调用:
● Decorator(b, c, a=99)

回到你的代码:
● partial 预设了: args=() , kwargs={'arg1': 1, ...}
● 调用时传入了: args=(method,) , kwargs={}
● 合并结果: args=(method,) , kwargs={'arg1': 1, ...}
● 最终执行: Decorator(method, arg1=1, ...)

*Tesla*的主页 *Tesla* | 小虾三级 | 园豆:1834
提问于:2026-06-25 05:53
< >
分享
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册