使用__slots__时类变量赋值覆盖所有实例同名属性的原因是什么?
该现象的底层原理
- 使用
__slots__声明属性时,Python会在类层面为每个声明的属性创建一个描述器对象,负责读写实例内存中预留的固定槽位数据。开启__slots__的类实例默认不会生成__dict__属性字典,所有声明属性的读写都由对应的类层面描述器完成。 - 正常读取实例的
name属性时,描述器的__get__方法会优先返回当前实例槽位中存储的值;如果实例槽位未赋值,才会降级到类层面的同名属性取值。 - 当你执行
Person.name = 'hacked'操作时,本质是把类层面原本的name描述器直接替换成了普通字符串类变量,原来负责读取实例槽位的逻辑被彻底覆盖。 - 此时再访问
p1.name/p2.name时,由于实例没有__dict__存储自有属性,会直接读取类层面的name字符串值,所以所有实例返回的都是同一个hacked结果。
补充验证:之前写入实例槽位的
mc、pp值并没有丢失,只要把类层面的name描述器恢复,就能重新读到原来的实例值,示例如下:# 提前保存原始描述器 orig_name = Person.name Person.name = 'hacked' print(p1, p2) # 输出 hacked hacked # 恢复描述器 Person.name = orig_name print(p1, p2) # 输出 mc pp
解释器未阻止该操作的原因
- Python的设计遵循"成年人共识"原则,默认信任开发者知道自己在做什么,不会做过多的语法层强制限制。
- 类层面给
__slots__同名属性赋值本身是合法的使用场景:可以通过类层面赋值为槽位属性设置全局默认值,所有未单独赋值的实例都会继承该默认值,这是官方允许的合理用法。 __slots__的设计目标只是优化内存占用、加快属性访问速度,并没有限制类属性修改的额外规则,所以解释器不会拦截对类层面槽位对应属性的赋值操作。
内容的提问来源于stack exchange,提问作者Marcin Cuprjak
相关产品推荐
相关产品推荐

