如果元组不包含 __iadd__ 但仍使用 INPLACE_ADD 指令,就地添加如何在元组中工作?

How does inplace add work in tuples if tuples do not include __iadd__ but still use INPLACE_ADD instruction?

我们知道 tuples 不支持项目分配,但我们可以用元组执行 inplace add,然而,这 创建一个新对象 ,如元组是不可变的。例如:

>>> t1 = (1, 2)
>>> id(t1)
154311048
>>> t1 += (3, 4)
>>> t1
(1, 2, 3, 4)
>>> id(t1)
157955320

一切都很好。另一方面,list 是可变的,在执行就地添加时不会创建新对象。

现在我的印象是 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

我推测,由于不能保证不可变对象的就地操作,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(我不知道为什么有人会这样做!此外,这并不能解释元组相对于列表的性能优势)或对于不可变对象,__iadd__ 可能只是回退到 __add__ 和 return 值,但是在执行 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也不建表!

如您所见,这让我非常困惑。谁能解释一下这是怎么回事,这是怎么回事?

如果 __iadd__ 不存在或 returns NotImplemented

INPLACE_ADD 将返回到 __add____radd__ 的常规加法。 =15=]