Python中属性名以__开头时@property setter不生效问题咨询
问题原因
该问题的根本原因是Python的**双下划线名称修饰(Name Mangling)**机制和getattr动态查找的行为差异导致的。
名称修饰规则
- Python会对类中定义的、满足
__开头 + 末尾最多1个下划线格式的标识符自动重命名,重命名规则为_类名__原标识符名,目的是避免类的私有属性被子类或外部意外覆盖。 - 仅当你在类的代码块中直接书写双下划线开头的属性名时,Python才会自动执行名称修饰;如果是通过字符串形式(比如
getattr的第二个参数、__dict__的键)传入的双下划线名称,不会触发修饰逻辑。
两段代码的行为差异分析
第一种写法(正常运行)
存储属性使用_a_,仅开头1个下划线,不满足名称修饰的触发条件,因此getattr查找的_a_和setter中赋值的self._a_是完全相同的属性,读写对应一致,运行正常。
第二种写法(setter不生效)
- setter中直接写
self.__a = value:属于类代码块内直接使用双下划线标识符,会被自动修饰为self._A__a = value,赋值操作实际是给实例新增了名为_A__a的属性。 - getter中使用
getattr(self, '__a', 0):'__a'是字符串参数,不会触发名称修饰,getattr会直接查找实例中是否存在名为__a的属性,而实际存储的是_A__a,所以永远找不到,一直返回默认值0,看起来就像setter没有生效。
验证方法
你可以在第二种写法的赋值后打印实例的属性字典验证上述逻辑:
a = A() a._a = 10 print(a.__dict__) # 输出:{'_A__a': 10}
可以看到存储的属性名是_A__a而非__a,和分析完全一致。
修复方案
提供两种常用修复方式:
方案1:getattr使用修饰后的属性名
class A: @property def _a(self): # 直接传入修饰后的名称字符串 return getattr(self, '_A__a', 0) @_a.setter def _a(self, value): self.__a = value
方案2:类内部直接访问属性(推荐)
避免用字符串动态查找,类内部直接访问双下划线属性时会自动触发名称修饰,同时在__init__中初始化属性避免访问报错:
class A: def __init__(self): self.__a = 0 @property def _a(self): return self.__a @_a.setter def _a(self, value): self.__a = value
内容的提问来源于stack exchange,提问作者gioxc88
相关产品推荐
相关产品推荐

