
本文介绍在不修改原有代码调用方式的前提下,通过代理列表(List Proxy)机制,使 dependent 列表自动响应 main 列表的增删改操作,并实时应用自定义转换逻辑(如数字转英文单词)。
本文介绍在不修改原有代码调用方式的前提下,通过代理列表(list proxy)机制,使 `dependent` 列表自动响应 `main` 列表的增删改操作,并实时应用自定义转换逻辑(如数字转英文单词)。
在遗留 Python 项目中,常遇到这样的需求:已有公开属性 main 被大量外部代码直接操作(如 t.main.append(1)),但新增功能要求其每次变更时,另一个属性 dependent 必须同步更新——且需经过特定转换(例如使用 inflect 将数字 1 转为 "one")。仅靠 @property + @setter 无法捕获原地修改(.append()、.remove()、+=、切片赋值等),因为这些操作不触发 setter,而是直接调用列表的底层方法。
最稳健的解决方案是自定义代理列表类(List Proxy),它继承 collections.abc.MutableSequence,封装原始列表并重写所有可变操作方法,在每次修改时自动更新关联数据:
import inflect
from collections.abc import MutableSequence
p = inflect.engine()
def transform(x):
return p.number_to_words(x) if isinstance(x, (int, float)) else str(x)
class SyncedList(MutableSequence):
def __init__(self, data=None, dependent_list=None, transform_func=transform):
self._data = data or []
self._dependent = dependent_list # 引用外部 dependent 列表
self._transform = transform_func
def _sync_dependent(self):
if self._dependent is not None:
self._dependent.clear()
self._dependent.extend(self._transform(item) for item in self._data)
# 实现 MutableSequence 抽象方法
def __getitem__(self, index):
return self._data[index]
def __setitem__(self, index, value):
self._data[index] = value
self._sync_dependent()
def __delitem__(self, index):
del self._data[index]
self._sync_dependent()
def __len__(self):
return len(self._data)
def insert(self, index, value):
self._data.insert(index, value)
self._sync_dependent()
class T:
def __init__(self):
self._main = [] # 真实存储
self.dependent = [] # 同步目标
self.main = SyncedList(self._main, self.dependent)
使用示例:
t = T() t.main.append(1) print(t.main) # [1] print(t.dependent) # ['one'] t.main.extend([2, 3]) print(t.main) # [1, 2, 3] print(t.dependent) # ['one', 'two', 'three'] t.main[0] = 10 print(t.main) # [10, 2, 3] print(t.dependent) # ['ten', 'two', 'three']
✅ 优势:
- 完全兼容原调用习惯(
t.main.append()、t.main[0] = ...等均生效); -
dependent始终与main严格同步,无需手动维护; -
dependent是真实list对象,会出现在t.__dict__中,满足序列化/跨服务传输需求; - 转换逻辑集中可控,易于扩展(如增加缓存、异步处理或条件过滤)。
⚠️ 注意事项:
- 若需支持更复杂操作(如
t.main += [4,5]),需额外实现__iadd__;若需支持sort()、reverse()等就地方法,也需显式重写; - 避免在
transform函数中执行耗时操作,否则会影响列表操作性能;必要时可结合functools.lru_cache缓存转换结果; - 此方案不改变
t.main的类型(仍是SyncedList实例),若强依赖isinstance(t.main, list),可在代理类中添加__class__ = list模拟(但属 hack,慎用)。
总结:当 @property 因性能或序列化限制不可行,而原地修改又无法被拦截时,代理列表是兼顾兼容性、实时性与工程健壮性的首选方案。它将“状态同步”从用户代码的责任,下沉为数据结构自身的能力,真正实现关注点分离。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











