Python dataclass设置IntEnum默认值时多实例属性同内存地址问题
问题解答
该行为属于Python的预期设计
你观察到的现象和dataclass本身无关,是Python两个核心特性的正常表现:
- 小整数驻留优化:Python为了降低频繁创建销毁小整数的性能开销,会默认缓存[-5, 256]区间内的所有整数对象,整个进程中所有用到这个区间内整数的地方,都会复用同一个缓存对象,因此默认值0的id在多个实例中完全相同。
- 枚举成员单例特性:所有继承自Enum的枚举类(包括IntEnum)的成员都是全局单例,同一个枚举值在进程中只会存在一个实例,因此多个实例引用同一个枚举成员时,id必然相同。
需要特别说明的是:int和IntEnum都属于不可变类型,即使多个实例共享同一个对象引用,也完全不会出现你可能担心的"改一个影响所有"的问题。对实例属性的赋值操作只会修改当前实例属性的指向,不会修改原对象内容,也不会联动影响其他实例的属性值,因此这个现象本身不会带来任何业务问题,你目前的额外赋值操作完全没有必要。
修改赋值后id不同的原因
你修改属性值后id变化,只是因为属性指向了新的对象:
- 如果你给两个实例的a属性都赋值为同一个小整数(比如10),你会发现两者的id会再次变为相同
- 如果你给两个实例的b属性都赋值为同一个枚举成员(比如E_Enum.First),两者的id也会再次相同
初始化阶段强制属性地址唯一的方法(不推荐,无实际价值)
如果你确实需要在初始化阶段就保证属性指向不同的对象,可以使用dataclass的field的default_factory参数,在每个实例初始化时生成新对象:
from dataclasses import dataclass, field from enum import IntEnum class E_Enum(IntEnum): Init = 0 First = 1 Second = 2 @dataclass class A(): # 每次初始化生成新的int对象,绕过小整数缓存 a: int = field(default_factory=lambda: int('0')) # 注意:枚举成员单例是语言层面强制的,无法生成两个值相同的不同IntEnum实例 # 如果需要同值不同id的枚举类对象,只能放弃使用IntEnum,自行实现类似功能的普通类 b: E_Enum = E_Enum.Init
注意这个操作除了浪费内存之外,不会带来任何实际收益,生产环境不建议这么做。
内容的提问来源于stack exchange,提问作者flok
相关产品推荐
相关产品推荐

