如何在Python装饰器中创建描述符实例以监听属性变更?
解决Python属性监听的装饰器改造方案
不用字符串传属性名的核心思路是利用占位变量的__name__属性提取属性名,再通过装饰器动态给目标类注入描述符实例。下面是完整实现步骤:
1. 完善描述符类(自动绑定属性名)
优化你的ObservedAccess描述符,利用Python 3.6+支持的__set_name__方法,自动把属性名绑定到描述符实例上,省去手动传递的麻烦:
class ObservedAccess: def __init__(self): # 存储属性更新回调,用于后续触发GUI更新等操作 self.update_callbacks = [] def __get__(self, instance, owner): # 从实例的__dict__取值,避免触发描述符无限递归 return instance.__dict__.get(self.name, None) def __set__(self, instance, value): old_val = instance.__dict__.get(self.name, None) instance.__dict__[self.name] = value # 触发所有注册的回调 for callback in self.update_callbacks: callback(attr_name=self.name, old_value=old_val, new_value=value) def __set_name__(self, owner, name): # 自动绑定属性名到描述符实例 self.name = name def on_update(self, callback): # 提供注册回调的接口 self.update_callbacks.append(callback)
2. 实现observableclass装饰器
装饰器接收占位变量作为参数,通过__name__提取属性名,动态给类注入ObservedAccess实例:
def observableclass(*attr_placeholders): def decorator(cls): for placeholder in attr_placeholders: # 从占位变量的__name__获取属性名 attr_name = placeholder.__name__ # 给类注入描述符实例 setattr(cls, attr_name, ObservedAccess()) return cls return decorator
3. 用法示例
先定义占位变量(名字对应要监听的属性名),再用装饰器修饰类:
# 定义占位变量,名字就是要监听的属性名 some_int = None some_string = None @observableclass(some_int, some_string) class TestObserving: pass # 测试:注册回调并修改属性 def gui_update(attr_name, old_value, new_value): print(f"GUI更新:属性[{attr_name}] 从 {old_value} 变为 {new_value}") obj = TestObserving() # 给some_int属性注册GUI更新回调 TestObserving.some_int.on_update(gui_update) # 修改属性触发回调 obj.some_int = 20 obj.some_string = "测试文本"
额外优化:避免占位变量污染命名空间
如果不想在全局定义占位变量,可以用辅助函数生成带名字的占位符:
def attr(name): """生成属性占位符的辅助函数""" def placeholder(): pass placeholder.__name__ = name return placeholder # 用法简化,无需全局变量 @observableclass(attr("some_int"), attr("some_string")) class TestObserving: pass
内容的提问来源于stack exchange,提问作者PaulCC
相关产品推荐
相关产品推荐

