我们知道tuple
不支持项目分配,但我们可以执行inplace add
然而,对于元组,这创建一个新对象,因为元组是不可变的。例如:
>>> t1 = (1, 2)
>>> id(t1)
154311048
>>> t1 += (3, 4)
>>> t1
(1, 2, 3, 4)
>>> id(t1)
157955320
这一切都很好。另一方面,list
s 是可变的,并且在执行就地添加时不会创建新对象。
现在我的印象是 python 中的就地添加是用 __iadd__
实现的魔法,所以:
>>> l1 = [1,2,3]
>>> l1 += [4, 5]
# is same as
>>> l1 = l1.__iadd__([4,5]) # 1
# but, again for lists, this is also same as, simply
>>> l1.__iadd__([4,5]) # 2
我推测,由于不可变对象(immutable对象)不能保证就地操作,因此 python 分配 l1.__iadd__([4,5])
至l1
,否则,对于列表,只需调用 l1.__iadd__([4,5])
而不将其分配回 l1
将修改 l1
到位。
因此,考虑到这一点,我猜元组可能会像这样工作,即,
>>> t1 = t1.__iadd__((3,4))
但是,
Traceback (most recent call last):
File "<ipython-input-12-e8ed2ace9f7f>", line 1, in <module>
t1.__iadd__((1,2))
AttributeError: 'tuple' object has no attribute '__iadd__'
确实,'__iadd__' in dir(tuple)
计算结果为 False
。然后我想,可能是在内部,Python 用元组创建了一个列表,执行 __iadd__
并将结果转换回tuple
(我不知道为什么有人会这样做!此外,这并不能解释元组相对于列表的性能优势)或对于不可变对象(immutable对象),__iadd__
可能只是回退到 __add__
并返回值,但在执行 dis
之后:
>>> from dis import dis
>>> dis('t1 += (3, 4)')
1 0 LOAD_NAME 0 (t1)
2 LOAD_CONST 0 ((3, 4))
4 INPLACE_ADD
6 STORE_NAME 0 (t1)
8 LOAD_CONST 1 (None)
10 RETURN_VALUE
但是字节码有指令INPLACE_ADD
!
对于列表,它是这样的:
>>> dis('l1 += [1,2]')
1 0 LOAD_NAME 0 (l1)
2 LOAD_CONST 0 (1)
4 LOAD_CONST 1 (2)
6 BUILD_LIST 2
8 INPLACE_ADD
10 STORE_NAME 0 (l1)
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
此外,如果是 list
有一个额外的指令BUILD_LIST
,所以tuples
也不要建立 list !
正如你所看到的,这对我来说非常令人困惑。有人可以解释一下发生了什么事吗?这是如何工作的?
最佳答案
如果__iadd__
不存在或返回
INPLACE_ADD
,则使用__add__
和__radd__
回退到常规加法未实现
。
关于python - 如果元组不包含 __iadd__ 但仍使用 INPLACE_ADD 指令,则就地添加如何在元组中工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60407384/