Python类的dict类属性为何在多个实例间共享数据?
问题原因解答
核心原理:类属性与实例属性的查找、修改逻辑
你遇到的现象是Python类属性与实例属性的差异,加上可变、不可变对象的操作特性共同导致的,和参数传递规则无关,核心是属性的查找与修改逻辑:
- 访问
self.xxx时,Python会优先查找当前实例的私有属性字典__dict__,如果找不到对应key,就会向上查找实例所属类的__dict__,仍找不到则继续沿继承链向上查找。 - 对不可变对象执行
self.xxx = yyy是赋值操作,会直接在当前实例上新增同名实例属性,不会修改类属性;对可变对象执行self.xxx[key] = yyy是原地修改操作,不会新增实例属性,会直接修改找到的属性指向的对象内容。
分属性解释表现
mystr表现符合预期的原因
- 你在B类中定义的
mystr = 'B'是类属性,类型为不可变的字符串 - A类
__init__中执行的self.mystr = 'A'是赋值操作,会在当前实例上新增一个名为mystr的实例属性,不会修改B类的类属性mystr - 每个B类实例初始化时,还未执行
super().__init__前,实例自身没有mystr属性,所以会读取到B类的类属性值B;执行super后实例有了自己的mystr实例属性值A,下一个新实例初始化时读取的类属性仍为B,符合预期。
mydict表现不符合预期的原因
- 你在B类中定义的
mydict = {}是类属性,类型为可变的字典 - A类
__init__中执行的self.mydict['key'] = 'value'是原地修改操作,不是赋值操作:此时实例自身还没有mydict实例属性,所以找到的是B类的类属性mydict,直接修改了这个类属性指向的字典对象的内容 - 第一个实例
b1初始化完成后,B类的类属性mydict已经被修改为{'key': 'value'},第二个实例b2初始化时,还未执行super前读取self.mydict,同样找不到实例属性,就会读取到已经被修改过的B类类属性,所以输出不是你预期的空字典。
修复方案
如果需要每个实例拥有独立的mydict,不要把mydict定义为类属性,改为在B类的__init__中初始化实例属性即可,修改后代码如下:
class A: def __init__(self): super().__init__() self.mystr = 'A' self.mydict['key'] = 'value' class B(A): mystr = 'B' def __init__(self, name): # 每个实例初始化独立的字典,作为实例属性存在,不共用类属性 self.mydict = {} print(name, 'mystr (before)', self.mystr) print(name, 'mydict (before)', self.mydict) super().__init__() print(name, 'mystr (after)', self.mystr) print(name, 'mydict (after)', self.mydict) if __name__ == '__main__': b1 = B('b1') b2 = B('b2')
修改后运行时,第二个实例b2初始化前读取的mydict就是独立的空字典,符合预期。
内容的提问来源于stack exchange,提问作者testo
相关产品推荐
相关产品推荐

