Python3.11带参数装饰器:类方法全局事件总线调用报错问题
解决方案:处理事件总线中类方法的self参数问题
问题核心是:类定义时用@on装饰的方法是未绑定的普通函数,事件总线触发时直接调用这个函数,没有传入实例的self参数,导致报错。下面是两种无需反射的通用解决方案:
方案1:用描述符自动绑定实例方法
通过让装饰器返回一个描述符对象,在实例访问方法时自动完成绑定并注册到事件总线。这种方式可以保留装饰器的写法,同时正确处理self参数:
import weakref # 全局事件总线存储 events = {} class EventListener: def __init__(self, event_name, callback): self.event_name = event_name self.callback = callback # 用弱引用字典存储实例与绑定方法的映射,避免内存泄漏 self.bound_callbacks = weakref.WeakKeyDictionary() def __get__(self, instance, owner): # 类访问时返回描述符自身 if instance is None: return self # 实例访问时,绑定方法并注册到事件总线 if instance not in self.bound_callbacks: bound_method = self.callback.__get__(instance, owner) events.setdefault(self.event_name, []).append(bound_method) self.bound_callbacks[instance] = bound_method return self.bound_callbacks[instance] def on(event_name): def decorator(callback): return EventListener(event_name, callback) return decorator def emit(event_name, *args, **kwargs): for callback in events.get(event_name, []): callback(*args, **kwargs) # 使用示例 class Ticking: @on("clock") def on_clock(self, clock): print(f"Clock ticked: {clock}") t = Ticking() emit("clock", 123) # 输出:Clock ticked: 123
说明:
- 描述符的
__get__方法会在实例访问该方法时被调用,此时能拿到实例对象instance,通过callback.__get__(instance, owner)生成绑定了self的实例方法。 - 用
weakref.WeakKeyDictionary存储绑定关系,当实例被垃圾回收时,对应的绑定方法会自动从字典中移除,避免内存泄漏。
方案2:在实例初始化时注册监听器
放弃类定义时的装饰器写法,改为在__init__中注册已经绑定了self的实例方法。这种方式更简单,无需修改装饰器:
events = {} def on(event_name): def decorator(callback): events.setdefault(event_name, []).append(callback) return callback return decorator def emit(event_name, *args, **kwargs): for callback in events.get(event_name, []): callback(*args, **kwargs) # 使用示例 class Ticking: def __init__(self): # 注册当前实例的on_clock方法(已绑定self) on("clock")(self.on_clock) def on_clock(self, clock): print(f"Clock ticked: {clock}") t = Ticking() emit("clock", 123) # 输出:Clock ticked: 123
说明:
- 在
__init__中注册的是已经绑定了实例的方法,事件总线触发时直接调用即可,不会缺少self参数。 - 缺点是需要手动在每个实例的初始化方法中注册监听器,不如装饰器写法直观。
内容的提问来源于stack exchange,提问作者Jacob Jewett
相关产品推荐
相关产品推荐

