Python列表:为什么在连接操作后会创建新的列表对象?

5

我正在阅读《Python学习手册》第五版中关于列表的主题。我注意到如果我们对列表进行拼接操作,它会创建一个新的对象。而extend方法不会创建新的对象,而是在原地进行改变。那么在拼接操作中实际上发生了什么呢?

例如:

l = [1,2,3,4]
m = l
l = l + [5,6]
print l,m

#output
([1,2,3,4,5,6], [1,2,3,4])

如果我使用增强赋值运算符,代码如下:
l = [1,2,3,4]
m = l
l += [5,6]
print l,m

#output
([1,2,3,4,5,6], [1,2,3,4,5,6])

在这两种操作中,后台发生了什么?
2个回答

6
在这里使用了两种方法:__add____iadd__l + [5, 6]l.__add__([5, 6])的简写。因此,l__add__ 方法返回与其他内容相加的结果。因此,l = l + [5, 6]l重新分配为l[5, 6]相加的结果。它不会影响m,因为你没有改变对象,而是重新定义名称。l += [5, 6]l.__iadd__( [5, 6] ) 的简写。在这种情况下,__iadd__ 更改了列表。由于m指向相同的对象,因此m也受到影响。 编辑: 如果未实现__iadd__,例如对于不可变类型(如tuplestr),则Python将使用__add__。例如,x += y将转换为x = x + y。另外,在x + y中,如果x未实现__add__,则将使用y.__radd__(x)(如果可用)。因此,x += y实际上在后台可能是x = y.__radd__(x)

1
注意,这里有一个重要的警告:不可变类型不实现__iadd__方法,而对于任何具有__add__但没有__iadd__的对象,Python会默默地使用__add__来执行操作。因此,在元组中,当您执行a = (1, 2)b=aa +=(3, 4)时,由于元组是不可变的,实际上就像最后一个语句是a = a + (3, 4)一样,即a被替换为一个全新的元组,而b继续引用原始的(1, 2)元组。 - ShadowRanger
不错。我喜欢在__radd__上的添加。 - ShadowRanger

5
您可以通过检查内存中引用的对象来更好地理解这一点。让我们使用 id 进行检查 在第一种情况下:
>>> l = [1,2,3,4]
>>> id(l)
4497052232
>>> m = l
>>> id(m)
4497052232
>>> l = l + [5,6]
>>> id(l)
4497052448
>>> print l, id(l), m, id(m)
[1, 2, 3, 4, 5, 6] 4497052448 [1, 2, 3, 4] 4497052232
>>> 

请注意,l = l + [5,6]会在内存中创建一个新对象,并且l将引用该对象。
在第二种情况下:
>>> l = [1,2,3,4]
>>> id(l)
4497052520
>>> m = l
>>> id(m)
4497052520
>>> l += [5,6]
>>> id(l)
4497052520
>>> print l, id(l), m, id(m)
[1, 2, 3, 4, 5, 6] 4497052520 [1, 2, 3, 4, 5, 6] 4497052520
>>> 

l += [5,6] 引用了内存中相同的对象。 因此得到了这个结果。 因此,基本上 += 等同于就地添加.. 可以在此处阅读更多内容


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接