为什么我的代码在使用__iadd__时会在包含项中生成__setitem__错误?
有没有人能告诉我,为什么这段代码会抱怨说容器里没有 __setitem__
呢?我以为只需要在容器里定义 __getitem__
来获取项目,然后用 __iadd__
来设置值就可以了,不明白为什么它还期待有 __setitem__
。
class Item:
def __init__(self):
pass
def __iadd__(self, value):
print 'added: ' + value
return self
class Container:
def __init__(self):
self.__items = {
'foo': Item()
}
def __getitem__(self, name):
return self.__items[name]
if __name__ == '__main__':
# works!
con = Container()
item = con['foo']
item += 'works!'
# wtf?
con['foo'] += "what's going on?"
# output:
# added: works!
# added: what's going on?
# Traceback (most recent call last):
# File "foo.py", line 27, in <module>
# con['foo'] += "what's going on?"
# AttributeError: Container instance has no attribute '__setitem__'
2 个回答
3
来猜猜这个问题:如果你对一个不可变类型,比如一个 int
,使用 +=
会发生什么?你能理解为什么对从 []
返回的值调用 +=
也需要调用 __setitem__
吗?
关键在于理解,Python 中任何包含 =
的操作符都涉及到赋值,因为它需要以预期的方式处理不可变类型。
为了让这个更清楚,考虑以下例子:
>>> a = 10*10**9
>>> b = a
>>> a is b
True
>>> a += 1
>>> a is b
False
很明显,a
被赋值为一个新值。因此,当你对 __getitem__
返回的值执行 +=
时,也会发生同样的事情。
不幸的是,Python 的文档在说明这会发生什么方面做得非常糟糕,但正如其他答案通过反编译所指出的那样,这确实会发生,并且必须这样做,以正确处理存储在可变容器中的不可变类型。
3
基本上,
con['foo'] += "what's going on?"
编译后会变成类似这样的东西:
item = con['foo']
item += "what's going on?"
conf['foo'] = item
你可以看到,反编译这个代码,得到的结果大概是:
2 0 LOAD_GLOBAL 0 (con)
3 LOAD_CONST 1 ('foo')
6 DUP_TOPX 2
9 BINARY_SUBSCR
10 LOAD_CONST 2 ("what's going on?")
13 INPLACE_ADD
14 ROT_THREE
15 STORE_SUBSCR