This question处理有关Python读写属性的__iadd__
。但是,我很难找到只读属性的解决方案。
在我的MWE中,我们有一个只读属性Beta.value
,返回一个Alpha
实例。我想我应该可以在__iadd__
上使用Beta.value
,因为返回的值已就地变异,并且对Beta
本身没有任何更改,很多喜欢" beta.value.content +=
"前面的一行。但是,以下代码与AttributeError: can't set attribute
崩溃。
是否可以在只读属性上使用__iadd__
?
class Alpha:
def __init__( self, content : int ) -> None:
self.content : int = content
def __iadd__( self, other : int ) -> "Alpha":
self.content += other
return self
class Beta:
def __init__( self ):
self.__value: Alpha = Alpha(1)
@property
def value( self ) -> Alpha:
return self.__value
beta = Beta()
beta.value.content += 2
beta.value += 2
答案 0 :(得分:3)
可以通过为只接受原始对象的属性添加特殊的setter来欺骗它。
班级Beta
将成为:
class Beta:
def __init__( self ):
self.__value: Alpha = Alpha(1)
def _get_val( self ) -> Alpha:
return self.__value
def _set_val( self, val: Alpha):
if not (val is self.__value): # only accept the existing object
raise AttributeError("can't set attribute")
value = property(_get_val, _set_val)
启用该黑客/技巧后,您可以成功使用:
>>> beta = Beta()
>>> beta.value.content
1
>>> beta.value = Alpha(2) # property IS read only
Traceback (most recent call last):
File "<pyshell#86>", line 1, in <module>
beta.value = Alpha(2)
File "<pyshell#78>", line 9, in _set_val
raise AttributeError("can't set attribute")
AttributeError: can't set attribute
>>> beta.value.content # and was not changed by an assignment attempt
1
>>> beta.value += 2 # but accepts augmented assignment
>>> beta.value.content
3
答案 1 :(得分:0)
人们经常忘记使用就地操作员的事情是他们的使用总是涉及一项任务。您可以在Alpha.content
(或任何int
或str
中)直观地看到这一点:整数是不可变的,但操作有效。忘记Alpha
或list
之类的步骤要容易得多,其中就地操作符只返回self
。只需记住操作符可以返回任何内容,结果必须绑定到原始名称。这里发生的事情基本上是这样的:
x = beta.value
x = operator.iadd(x, 2) # Totally fine
beta.value = x # You can imagine how this would be a problem...
这样做的直接后果是,即使出现错误,您也会看到beta.value
中的更改。
您总是欢迎通过首先分配临时变量来绕过重新分配,即明确地运行上面显示的前两行。请记住,虽然在您的情况下Alpha
是可变的并且实际上就地修改了,但这不是一般情况的要求:
x = beta.value
x += 2
按预期工作。然而,
x = beta.value.content
x += 2
没有,因为int.__iadd__
不可避免地会返回一个新的引用。
答案 2 :(得分:0)
运算符 += 处理整个分配,整个行。因此存在歧义:您的对象是调用 __iadd__ 还是 setter 属性?看看这个:
这不起作用:
beta.value += 2 # calls setter property (which does not exist)
但这有效:
value = beta.value # calls getter property
value += 2 # calls __iadd__ (upon the returned object from the getter)
注意:这让我很沮丧,但它必须是这样;否则,我们如何知道调用了哪个方法以及代码的行为?
玩得开心:)