我创建了一个Python列表
>>> list1 = ['a', 'b', 'c']
并设置
>>> list2 = list1
现在我对list1
和list2
>>> list1 = list1 + [1, 2, 3]
>>> list1
['a', 'b', 'c', 1, 2, 3]
>>> list2
['a', 'b', 'c']
和
>>> list2 += [1,2,3]
>>> list1
['a', 'b', 'c', 1, 2, 3]
>>> list2
['a', 'b', 'c', 1, 2, 3]
但两种情况的结果都不同。它是什么原因?
答案 0 :(得分:2)
Python中用于列表的+=
运算符实际上是在内部调用list.extend()
函数,因此列表已扩展到位。
当我们执行+
连接运算符时,会创建并返回一个新列表,因此list1
中的实际列表不会更改,而list1
现在指向一个新的清单。
答案 1 :(得分:1)
基于Ned Batchelder关于Pycon 2015的演讲:
列表中的 __iadd__
实现为扩展实际实例(根据Ned,它是CPython的非记录行为)。在list1 = list2
之后,两个名称都引用相同的实例 - 因此在第二个名称下可以看到扩展实例。
__add__
实际上是根据两个输入列表创建一个新列表。
作为证明,请考虑以下代码段:
import dis
def f1():
list1 += [1,2,3]
def f2():
list1 = list1 + [1,2,3]
dis.dis(f1)
dis.dis(f2)
让我们检查输出:
>>> dis.dis(f1)
2 0 LOAD_FAST 0 (list1)
3 LOAD_CONST 1 (1)
6 LOAD_CONST 2 (2)
9 LOAD_CONST 3 (3)
12 BUILD_LIST 3
15 INPLACE_ADD
16 STORE_FAST 0 (list1)
19 LOAD_CONST 0 (None)
22 RETURN_VALUE
>>> dis.dis(f2)
2 0 LOAD_FAST 0 (list1)
3 LOAD_CONST 1 (1)
6 LOAD_CONST 2 (2)
9 LOAD_CONST 3 (3)
12 BUILD_LIST 3
15 BINARY_ADD
16 STORE_FAST 0 (list1)
19 LOAD_CONST 0 (None)
22 RETURN_VALUE
如您所见,+=
使用INPLACE_ADD
,其中l1 + l2
没有。{/ p>
答案 2 :(得分:1)
这背后的原因是+=
和+
调用了该类的两种不同方法,__iadd__
method和__add__
method。
从API的角度来看, iadd 应该用于修改可变对象(返回已变异的对象),而添加应该返回一个新的实例一些东西。对于不可变对象,两个方法都返回一个新实例,但 iadd 会将新实例放在当前命名空间中,其名称与旧实例的名称相同。这就是为什么
i = 1
i += 1
似乎增加了我。实际上,你得到一个新的整数并将它“分配给”i - 失去一个对旧整数的引用。在这种情况下,i + = 1与i = i + 1完全相同。但是,对于大多数可变对象,它是一个不同的故事:
作为一个具体的例子:
a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a #[1, 2, 3, 1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
与之相比:
a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
注意在第一个例子中,因为b和引用相同的对象,当我在b上使用+ =时,它实际上改变了b(并且看到了这个改变 - 毕竟,它引用了相同的列表)。然而,在第二种情况下,当我执行b = b + [1,2,3]时,这将采用b引用的列表并将其与新列表[1,2,3]连接。然后它将连接列表存储在当前命名空间中作为b - 不考虑之前的行是什么。
答案 3 :(得分:1)
这是因为您正在使用第一个操作向list1
分配新对象,而您正在使用第二个操作更改分配给list2
的原始对象。
如果您使用id()
检查分配给变量的对象的ID,则更容易理解:
>>> list1 = ['a', 'b', 'c']
>>> id(list1)
4394813200
>>> list2 = list1
>>> id(list2)
4394813200 # same id
>>> list1 = list1 + [1, 2, 3]
>>> id(list1)
4394988392 # list1 now references another object
>>> list1
['a', 'b', 'c', 1, 2, 3]
>>> id(list2)
4394813200 # list2 still references the old one
>>> list2
['a', 'b', 'c']
>>> list2 += [1,2,3]
>>> id(list2)
4394813200 # list2 still references the old one
>>> list2
['a', 'b', 'c', 1, 2, 3]
>>> id(list1)
4394988392
>>> list1
['a', 'b', 'c', 1, 2, 3]