如何实现类property更新时自动同步其在类字典中的对应取值
问题原因说明
你原有代码的问题是__init__中给some_dict的键赋值时,仅存储了当时self.id的瞬时值None,属于值拷贝,后续property属性更新后,字典里存储的旧值不会自动同步变更。以下是几种不用逐个编写setter同步逻辑的自动化实现方案:
解决方案
方案1:重写__setattr__统一拦截属性赋值
全局拦截所有属性赋值操作,只要属性名属于需要同步的键集合,就自动更新到内部字典中,仅需维护同步键列表即可:
class Test(): def __init__(self): self._id = None self._name = None # 定义所有需要同步到some_dict的property名称 self.sync_keys = {"id", "name"} self.some_dict = {key: getattr(self, key) for key in self.sync_keys} @property def id(self): return self._id @id.setter def id(self, new_id: int): self._id = new_id @property def name(self): return self._name @name.setter def name(self, new_name: str): self._name = new_name def __setattr__(self, key, value): # 先执行原生属性赋值逻辑 super().__setattr__(key, value) # 排除初始化阶段变量未创建的情况 if hasattr(self, "sync_keys") and hasattr(self, "some_dict"): if key in self.sync_keys: self.some_dict[key] = value # 测试 tst = Test() tst.id = 112 tst.name = "测试" print(tst.id) # 112 print(tst.some_dict) # {'id': 112, 'name': '测试'}
优点:新增同步属性仅需要把属性名添加到sync_keys集合即可,不需要修改其他逻辑。
方案2:封装自动同步的通用描述符
把同步逻辑封装为自定义描述符,代替原生property使用,新增属性仅需一行声明:
class SyncProperty: def __init__(self, target_dict="some_dict"): self.target_dict = target_dict self._storage_name = None def __set_name__(self, owner, name): self._storage_name = f"_{name}" self.prop_name = name def __get__(self, instance, owner): return getattr(instance, self._storage_name) if instance else self def __set__(self, instance, value): setattr(instance, self._storage_name, value) # 自动同步到指定字典 if hasattr(instance, self.target_dict) and self.prop_name in getattr(instance, self.target_dict): getattr(instance, self.target_dict)[self.prop_name] = value class Test(): # 直接用自定义描述符声明属性,无需单独写getter/setter id = SyncProperty() name = SyncProperty() def __init__(self): self._id = None self._name = None self.some_dict = {"id": self.id, "name": self.name} # 测试 tst = Test() tst.id = 112 tst.name = "demo" print(tst.id) # 112 print(tst.some_dict) # {'id': 112, 'name': 'demo'}
优点:完全无侵入,新增同步属性不需要写任何额外逻辑,适合有大量同步属性的场景。
方案3:动态取值字典(无需主动同步)
如果不需要持久存储字典值,仅访问时需要拿到最新属性值,可以把some_dict改为动态映射,访问时实时读取属性当前值:
from collections.abc import MutableMapping class DynamicDict(MutableMapping): def __init__(self, instance, map_keys): self.instance = instance self.map_keys = set(map_keys) def __getitem__(self, key): if key not in self.map_keys: raise KeyError(key) return getattr(self.instance, key) def __setitem__(self, key, value): if key not in self.map_keys: raise KeyError(key) setattr(self.instance, key, value) def __delitem__(self, key): raise NotImplementedError("不支持删除键") def __iter__(self): return iter(self.map_keys) def __len__(self): return len(self.map_keys) class Test(): def __init__(self): self._id = None self._name = None # 绑定动态字典,指定要映射的属性名 self.some_dict = DynamicDict(self, ["id", "name"]) @property def id(self): return self._id @id.setter def id(self, new_id: int): self._id = new_id @property def name(self): return self._name @name.setter def name(self, new_name: str): self._name = new_name # 测试 tst = Test() tst.id = 112 print(tst.some_dict["id"]) # 112 print(dict(tst.some_dict)) # 转普通字典也能拿到最新值 {'id': 112, 'name': None} tst.some_dict["id"] = 223 # 支持通过字典反向更新属性 print(tst.id) # 223
优点:永远返回最新值,不需要任何同步逻辑,还支持字典和属性的双向同步。
内容的提问来源于stack exchange,提问作者DisplayedName
相关产品推荐
相关产品推荐

