如何在类的实例变量上使用Python装饰器注册回调函数?
这个问题我之前也踩过坑,核心是没搞清楚Python装饰器的执行时机!先给你说清楚为什么你第二种写法不行,再给你两个实用的解决方案。
为什么直接用@self.class_b.register_callback()会报错?
Python的装饰器是在类定义的阶段就执行的——也就是Python刚读到ClassA的代码、解析do_something方法的时候,装饰器就会运行了。但self是在ClassA被实例化(也就是调用__init__方法)之后才会存在的变量,这时候self.class_b根本还没被创建,自然会报self未定义的错误。
方案1:手动在__init__里注册回调(最直接)
既然装饰器在类定义时用不了,那我们换个思路:等ClassA实例化完成、self.class_b已经存在的时候,再手动把方法注册进去。代码改成这样:
import ClassB class ClassA(): def __init__(self): self.class_b = ClassB() # 实例化完成后,手动把do_something注册为回调 self.class_b.register_callback()(self.do_something) def do_something(self): # 这里写你的业务逻辑 print("执行do_something")
这个写法简单直接,完全满足你的需求:每个ClassA实例都有自己独立的ClassB实例,do_something也会被注册到对应实例的回调列表里。
方案2:标记装饰器批量注册(多回调场景更方便)
如果你的ClassA里有好几个需要注册的回调方法,一个个手动注册太麻烦,可以先给这些方法打个标记,然后在__init__里统一扫描注册:
# 先写一个极简的标记装饰器,只给方法加个标记属性 def callback(fn): fn._is_callback = True return fn import ClassB class ClassA(): def __init__(self): self.class_b = ClassB() # 遍历当前实例的方法,把标记过的自动注册 for name, method in vars(self).items(): if callable(method) and hasattr(method, '_is_callback'): self.class_b.register_callback()(method) @callback def do_something(self): print("执行do_something") @callback def do_another_task(self): print("执行另一个任务")
这样不管你加多少个回调方法,只要给方法加上@callback标记,初始化时就会自动注册到self.class_b里,维护起来省心很多。
顺便解释下为什么模块级别的写法能行
你第一种写法里,模块级别的class_b是在ClassA定义之前就创建好的,当Python解析@class_b.register_callback()时,class_b已经是一个存在的ClassB实例了,所以装饰器能正常运行。但这个写法有个隐藏问题:所有ClassA实例都会共享同一个class_b的回调列表,这显然不是你想要的(你要的是每个ClassA实例有自己的class_b),所以虽然能跑,但不符合你的真实需求。
总结一下:核心就是避开“类定义阶段使用实例变量”这个矛盾,要么实例化后手动注册,要么用标记批量注册,这两种方式都能完美实现你的目标。
备注:内容来源于stack exchange,提问作者bchiddy

