类变量引发引用类型共享污染,正确做法是在__init__中初始化实例变量;需共享时用不可变对象或深拷贝;多线程下需加锁或使用线程安全结构。

子类实例修改继承自父类的引用类型变量(如 list、dict、set、自定义对象等)时,若该变量是**类变量**(即定义在类体中、未绑定到 self 的变量),所有实例会共享同一份内存地址——这正是“全量污染”的根源:一个子类实例的修改,会意外影响其他所有父类/子类实例的状态。
关键问题定位:分清类变量 vs 实例变量
污染只发生在引用类型被错误声明为类变量时。例如:
❌ 危险写法(引发污染):
class Parent:
shared_list = [] # ← 类变量!所有实例共用同一个 list 对象
shared_dict = {}
<p>class Child(Parent):
def append_item(self, x):
self.shared_list.append(x) # 修改的是类变量,非当前实例独有
</p>
此时 Child().append_item(1) 后,Parent.shared_list、Child().shared_list、甚至另一个 Child() 实例的 shared_list 全变成 [1]。
正确做法:确保每个实例拥有独立副本
必须将引用类型初始化移到 __init__ 中,作为实例变量:
- 在父类
__init__中为每个实例创建新对象:self.shared_list = []或self.shared_list = list() - 避免在类体顶层直接赋值可变对象(
[]、{}、set()等) - 若需默认值,可用
None占位再惰性初始化:def __init__(self, data=None):<br> self.data = data if data is not None else []
特殊情况:需共享但又防污染?用不可变包装或深拷贝
当业务逻辑确实需要“共享初始模板”,但各实例后续操作应互不干扰,可:
- 用元组
()或冻结集合frozenset()作类变量(只读,无法污染) - 在实例化时主动拷贝:
self.config = copy.deepcopy(Parent.DEFAULT_CONFIG) - 对 Java/PHP 等语言,使用
new ArrayList()或array_merge([], self::$default)替代直接引用静态数组
多线程环境额外加固
即使已转为实例变量,若多个线程并发修改同一实例的引用类型字段,仍可能引发竞态。此时需:
- 加锁(
threading.Lock或synchronized块) - 改用线程安全结构(如
queue.Queue、concurrent.futures工具类) - 采用不可变数据结构(如 Python 的
immutables.Map、Java 的ImmutableList)










