Python实现MainClass感知其持有的SubClass实例属性变更的方法
解决方案
核心思路是采用观察者模式,让子属性类在自身属性变更时主动通知父类,无需额外的拷贝对比开销,也支持多属性扩展。
方案1:基础回调通知实现
这个方案适配你当前的代码结构,改动最小:
首先给SubClass增加变更回调钩子,属性修改时触发回调通知父类:
class SubClass: def __init__(self, on_change=None): self._param = None # 存储变更回调函数,触发时会传入[变更属性名, 新值] self.on_change = on_change @property def param(self): return self._param @param.setter def param(self, value): print('set param') self._param = value # 属性变更后触发回调 if self.on_change: self.on_change("param", value)
然后MainClass在初始化子实例时传入回调方法,接收变更通知:
class MainClass: def __init__(self): # 初始化子实例时传入自身的通知处理方法 self._var = SubClass(on_change=self._on_var_prop_change) @property def var(self): print('read var') return self._var @var.setter def var(self, value): print('set var') self._var = value # 如果是替换整个子实例,要给新实例重新绑定回调 self._var.on_change = self._on_var_prop_change # 子属性变更的处理逻辑,在这里可以感知到所有子属性的修改 def _on_var_prop_change(self, prop_name, new_value): print(f"MainClass感知到var的属性{prop_name}被修改为{new_value}")
测试运行你给出的用例:
cls = MainClass() cls.var.param = 3
输出结果:
read var set param MainClass感知到var的属性param被修改为3
扩展适配场景
1. 多子属性/多子实例的情况
如果MainClass有多个类似var的子属性,只需要给不同子实例绑定不同的回调,或者在回调中增加实例标识即可:
# 回调可以增加实例标识参数 def _on_sub_prop_change(self, sub_instance_name, prop_name, new_value): print(f"MainClass感知到{sub_instance_name}的属性{prop_name}被修改为{new_value}") # 初始化不同子实例时传入不同标识 self._var1 = SubClass(on_change=lambda name, val: self._on_sub_prop_change("var1", name, val)) self._var2 = SubClass(on_change=lambda name, val: self._on_sub_prop_change("var2", name, val))
2. 数组类型属性修改元素的感知
如果param是数组,直接修改数组元素(如cls.var.param[0] = 5)不会触发property的setter,此时可以自定义可观察数组类:
class ObservableList(list): def __init__(self, *args, on_change=None, **kwargs): super().__init__(*args, **kwargs) self.on_change = on_change def __setitem__(self, key, value): super().__setitem__(key, value) if self.on_change: self.on_change(f"索引{key}修改", value) def append(self, value): super().append(value) if self.on_change: self.on_change("追加元素", value) # 其他需要监听的修改方法比如pop、insert、extend等按同样逻辑重写即可
在SubClass的param setter中给数组绑定回调即可:
@param.setter def param(self, value): print('set param') # 如果赋值的是普通列表,自动转为可观察列表 if isinstance(value, list) and not isinstance(value, ObservableList): value = ObservableList(value, on_change=lambda act, val: self.on_change(f"param.{act}", val)) self._param = value if self.on_change: self.on_change("param", value)
通用优化方案
如果子属性类有大量属性需要监听,可以通过元类或者描述符统一注入通知逻辑,无需每个属性的setter都手写回调触发代码,进一步降低重复代码量。
内容的提问来源于stack exchange,提问作者mauro
相关产品推荐
相关产品推荐

