Python 使用嵌套的特殊可调用类型会引发TypeError:。。。对象不支持项分配
考虑下面的类Python 使用嵌套的特殊可调用类型会引发TypeError:。。。对象不支持项分配,python,python-3.x,Python,Python 3.x,考虑下面的类 class Spam: def __init__(self): from collections import defaultdict self.eggs = defaultdict(list) def __getitem__(self, index): class AdderHelper: def __init__(self, eggs): self.eggs
class Spam:
def __init__(self):
from collections import defaultdict
self.eggs = defaultdict(list)
def __getitem__(self, index):
class AdderHelper:
def __init__(self, eggs):
self.eggs = eggs
def __iadd__(self, egg):
self.eggs.append(egg)
return self
return AdderHelper(self.eggs[index])
为了提供一个索引就地add操作符,我定义了一个Helper类(类似于迭代器模式),并提供了要在该类的实例上调用的下一级操作符
例如,我打算提供以下操作员行为
spam = Spam()
spam[0] += 1
不幸的是,Python不喜欢它,并且抱怨
Traceback (most recent call last):
File "<ipython-input-82-ee0bba1041e3>", line 1, in <module>
spam[0] = 1
TypeError: 'Spam' object does not support item assignment
看起来,我根本不理解这种行为
然而,对于就地添加,它确实调用了setitem,克服它的唯一方法是重写setitem函数,如中所示
def __setitem__(self, index, value):
obj = self.__getitem__(index)
obj += value
return self
注意
Q1:@vaultah spam[0]+=1基本上是spam[0]=spam[0]+1
我不能;找不到任何文档支持python将就地运算符视为二进制运算,第一个操作数为self。就连拆卸也不这么说
def foo(spam):
spam[0] += 1
dis.dis(foo)
2 0 LOAD_FAST 0 (spam)
2 LOAD_CONST 1 (0)
4 DUP_TOP_TWO
6 BINARY_SUBSCR
8 LOAD_CONST 2 (1)
10 INPLACE_ADD
12 ROT_THREE
14 STORE_SUBSCR
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
+=
是一个扩展赋值运算符,它调用LHS的\uuuIdd\uIdd
方法(如果存在),然后将其返回值赋值给LHS:
例如,如果x是一个类的实例,该类的
方法,x+=y
相当于x=x.。\uuuu iadd\uuuu(y)
()
因此
相当于
spam = Spam()
spam[0] = spam[0].__iadd__(1)
虽然spam[0]
成功是因为实现了\uuuu getitem\uuuuu
,但对spam[0]
的后续分配不会成功,因为它需要\uuuuu setitem\uuuuu
才能工作。同时,spam[0].eggs
列表也会更新,因为\uuuuuidd\uuuu
按预期工作:
In [4]: spam[0].eggs
Out[4]: [1]
有关常见问题,请参阅。
+=
是一个增强赋值运算符,它调用LHS的\uuuu iadd\uuuu
方法(如果存在),然后将其返回值赋值给LHS:
例如,如果x是一个类的实例,该类的
方法,x+=y
相当于x=x.。\uuuu iadd\uuuu(y)
()
因此
相当于
spam = Spam()
spam[0] = spam[0].__iadd__(1)
虽然spam[0]
成功是因为实现了\uuuu getitem\uuuuu
,但对spam[0]
的后续分配不会成功,因为它需要\uuuuu setitem\uuuuu
才能工作。同时,spam[0].eggs
列表也会更新,因为\uuuuuidd\uuuu
按预期工作:
In [4]: spam[0].eggs
Out[4]: [1]
请参阅相关常见问题解答。
spam[0]=1
调用\uuuuu setitem\uuuuuuuuuuu
,而不是\uuuuuuuu getitem\uuuuuuuuuuuuu
@vaultah:很抱歉,出现了一个印刷错误,我的观点仍然站得住脚spam[0]+=1
基本上就是spam[0]=spam[0]+1
@vaultah。我不能;我找不到任何引用,它说python将把一个就地运算符当作一个二进制运算,第一个操作数是self。甚至反汇编也会说:是的,对不起,我跳过了\uuuu iadd\uuuuuu
部分spam[0]=1
调用\uuuuu setitem\uuuuuuuu
@vaultah,而不是\uuu getitem\uuuuuuuu
:对不起,有一个印刷错误,我的观点仍然站得住脚spam[0]+=1
基本上就是spam[0]=spam[0]+1
@vaultah。我不能;我找不到任何引用,它说python将把一个就地运算符当作一个二进制运算,第一个操作数是self。甚至反汇编也会说:是的,对不起,我跳过了\uu\uud\uu
部分