因为类属性在定义时初始化且所有实例共享同一可变对象,导致修改一个实例影响其他实例;正确做法是将可变对象初始化移至__init__中,确保每个实例拥有独立副本。

为什么 list、dict 作为类属性会导致实例间数据污染?
Python 中类属性在类定义时就完成初始化,且只执行一次。如果用可变对象(比如 [] 或 {})直接赋值给类属性,所有实例会共享这个同一对象。不是“每个实例都拿到一份副本”,而是“所有实例指向内存里同一个列表”。
常见错误现象:obj1.items.append(1) 后,obj2.items 也多出一个 1;调试时发现修改一个实例的属性,其他实例跟着变。
根本原因不是 Python 的 bug,而是对“类属性生命周期”和“可变对象引用语义”的误判。
正确做法:把可变默认值移到 __init__ 中初始化
必须确保每次创建实例时,都生成独立的新对象。唯一可靠的位置是 __init__ 方法内部。
- ❌ 错误写法:
class Bag: items = [] # 所有实例共用这一个 list - ✅ 正确写法:
class Bag: def __init__(self): self.items = [] # 每个实例都有自己的 list - 如果需要带参数初始化,也在这里处理:
self.items = items or [],但注意避免传入None以外的假值干扰逻辑
函数默认参数陷阱:和类属性是同一类问题
这个 Bug 不仅出现在类属性,也高频出现在函数定义中——def add(item, cache=[]) 是经典反模式。原理完全一致:默认参数在函数定义时求值一次,后续调用复用同一对象。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
修复方式同样是在函数体内初始化:
def add(item, cache=None):
if cache is None:
cache = []
cache.append(item)
return cache
注意:is None 判断比 not cache 更安全,防止传入空列表却被误判为“未提供”。
哪些类型可以放心当类属性?
不可变对象(immutable)作为类属性是安全的,因为无法原地修改。哪怕多个实例“共享”,也不会出现数据污染。
- 安全类型:
int、str、tuple、None、True、False - 不安全类型:
list、dict、set、自定义可变类实例 - 特别注意:
tuple看似安全,但如果它内部包含可变对象(如([],)),依然会污染——判断依据是“是否可能被原地修改”,而非表面类型
最省心的做法:只要不确定是否可变,一律放进 __init__;类属性只放真正需要全局共享的常量或配置项。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










