Weird behavior in python list concatenation
我创建一个python列表
1 | >>> list1 = ['a', 'b', 'c'] |
并设置
1 | >>> list2 = list1 |
现在我执行两个类似的操作,分别是
1 2 3 4 5 | >>> list1 = list1 + [1, 2, 3] >>> list1 ['a', 'b', 'c', 1, 2, 3] >>> list2 ['a', 'b', 'c'] |
和
1 2 3 4 5 | >>> list2 += [1,2,3] >>> list1 ['a', 'b', 'c', 1, 2, 3] >>> list2 ['a', 'b', 'c', 1, 2, 3] |
但两种情况的结果都不一样。原因是什么?
python for list中的
而当我们执行
这是因为您在第一个操作中为
如果使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | >>> 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] |
这背后的原因是
从API的角度来看,iadd应该被用于在适当的位置修改可变对象(返回发生变化的对象),而add应该返回某个对象的新实例。对于不可变的对象,这两个方法都返回一个新实例,但IADD会将新实例放在当前命名空间中,其名称与旧实例的名称相同。这就是为什么
1 2 | i = 1 i += 1 |
实际上,你得到了一个新的整数,并在"i"的顶部分配它——丢失了一个对旧整数的引用。在这种情况下,i+=1与i=i+1完全相同。但是,对于大多数可变对象,情况就不同了:
作为一个具体的例子:
1 2 3 4 5 | 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] |
相比:
1 2 3 4 5 | 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和A引用同一个对象,当我在B上使用+=时,它实际上改变了B(A也看到了改变——毕竟,它引用的是同一个列表)。然而,在第二种情况下,当我做b=b+[1,2,3]时,这将获取b引用的列表,并将其与新列表[1,2,3]连接。然后,它将连接的列表存储在当前名称空间中为b——而不考虑之前的行是什么。
根据内德·巴切尔德在2015年Pycon上的演讲:
清单上的
作为证明,请考虑以下代码片段:
1 2 3 4 5 6 7 8 9 10 | import dis def f1(): list1 += [1,2,3] def f2(): list1 = list1 + [1,2,3] dis.dis(f1) dis.dis(f2) |
让我们检查输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | >>> 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 |
如你所见,