Python中<instance>.<property> += v会调用属性的setter吗?
为什么对Python属性调用
__iadd__会触发setter? 首先看定义的类代码:
class TameWombat: def __init__(self): self.stomach = [] def __iadd__(self, v): self.stomach += v return self class Fred: def __init__(self): self._pet = TameWombat() @property def wombat(self): return self._pet @wombat.setter def wombat(self, v): raise ValueError("Fred只想要这只特定的袋熊,谢谢。")
尝试对属性调用__iadd__方法时出现异常:
>>> fred = Fred() >>> >>> fred.wombat += '美味的食物' Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in wombat ValueError: Fred只想要这只特定的袋熊,谢谢。
明明这里的+=应该作用于getter返回的袋熊实例,而不是直接修改Fred的属性,为什么会触发setter?
原因解析
Python对+=这类增强赋值运算符的处理是固定流程:
- 第一步:调用属性的getter,获取当前值,记为临时变量
temp = fred.wombat - 第二步:执行
temp = temp.__iadd__('美味的食物'),哪怕你的__iadd__返回了实例本身,Python也不会跳过后续步骤 - 第三步:尝试调用属性的setter,把
temp重新赋值给fred.wombat,也就是fred.wombat = temp
哪怕temp和原来的袋熊实例是同一个对象,Python也会严格执行第三步的赋值操作,所以就触发了setter里的异常。
解决办法
有两种可行的处理方式:
- 如果不需要限制外部替换Fred的袋熊实例,直接删掉setter方法即可。这样第三步找不到setter,就不会执行赋值,自然不会报错
- 要保留setter的限制的话,直接调用
__iadd__方法代替+=语法:
这种方式只会执行前两步,不会触发后续的赋值操作,也就不会触发setter。fred.wombat.__iadd__('美味的食物')
内容的提问来源于stack exchange,提问作者Marnanel Thurman
相关产品推荐
相关产品推荐

