类属性用[]会引发意外共享,因为类属性在定义时只初始化一次,所有实例共用同一可变对象;正确做法是在__init__中用none默认值惰性初始化,或用@property延迟赋值。

为什么类属性用 [] 会引发意外共享?
因为类属性在类定义时只初始化一次,所有实例共用同一份对象。当你把 list、dict 或 set 这类可变对象直接赋给类属性,后续任意实例调用 .append() 或 .update(),都会改写这个共享对象——不是“每个实例有自己的空列表”,而是“所有实例在往同一个列表里塞数据”。
正确做法:用 None 作默认值,在 __init__ 中创建新对象
这是最常用也最安全的模式。避免在类体中直接写 items = [],改用惰性初始化:
class Box:
def __init__(self, items=None):
self.items = items if items is not None else []
这样每次实例化都拿到独立的 []。注意必须用 is not None 判断,不能用 if not items——否则传入空列表 [] 也会被覆盖。
想支持类方法或属性访问时也自动初始化?用 @property + 延迟赋值
如果某个类属性需要被多个实例按需读取,又不想在 __init__ 里强制初始化(比如开销大或依赖运行时状态),可以用 @property 封装:
class Cache:
def __init__(self):
self._data = None
@property
def data(self):
if self._data is None:
self._data = []
return self._data
这样首次访问 cache.data 才创建列表,且每个实例互不干扰。别忘了用私有变量 _data 存储实际值,避免外部直接修改。
误用 defaultdict 或 list 作为类属性会怎样?
哪怕写成 from collections import defaultdict; cache = defaultdict(list),只要它是类属性,所有实例仍共享同一个 defaultdict 实例。同理,items = list() 和 items = [] 在类定义里效果完全一样——都是单次求值、全局共享。真正起作用的永远是“在哪执行、执行几次”,而不是“写法看起来多函数式”。
关键不是记住“别用列表当类属性”,而是理解 Python 类定义阶段的执行时机:类体代码只跑一遍,所有可变对象字面量([]、{}、set())都在此时生成并绑定到类上。绕不开这点,就只能靠 __init__ 或 @property 把对象创建推迟到实例层面。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











