如果元组不包含 __iadd__ 但仍使用 INPLACE_ADD 指令,就地添加如何在元组中工作?
How does inplace add work in tuples if tuples do not include __iadd__ but still use INPLACE_ADD instruction?
我们知道 tuple
s 不支持项目分配,但我们可以用元组执行 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=]
我们知道 tuple
s 不支持项目分配,但我们可以用元组执行 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=]