python列表连接中的奇怪行为
我创建了一个Python列表,如下所示python列表连接中的奇怪行为,python,list,Python,List,我创建了一个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'] 及 但两种情况下的结果都不同。原
>>> list1 = ['a', 'b', 'c']
设定
>>> list2 = list1
现在,我对list1
和list2
执行两个类似的操作
>>> list1 = list1 + [1, 2, 3]
>>> list1
['a', 'b', 'c', 1, 2, 3]
>>> list2
['a', 'b', 'c']
及
但两种情况下的结果都不同。原因是什么?根据Ned Batcheld关于Pycon 2015的演讲: 列表上的
\uuuu iadd\uuuu
是作为扩展实际实例实现的(根据Ned,它的CPython的非文档化行为)。在list1=list2
之后,两个名称都引用同一个实例-因此扩展实例在第二个名称下可见
\uuuu添加\uuuu
实际上是基于两个输入列表创建一个新列表
作为一个证明,考虑下面的代码片段:
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\u ADD
,而l1+l2
没有使用。Python中的列表操作符实际上在内部调用了list.extend()
函数,因此列表被就地扩展
然而,当我们使用
+
串联运算符时,会创建并返回一个新的列表,因此list1
中的实际列表不会更改,而是list1
现在指向一个新的列表。这背后的原因是+=
和+
调用类的两个不同方法,和
从API的角度来看,iadd应该用于修改原地可变的对象(返回突变的对象),而add应该返回某个对象的新实例。对于不可变对象,这两个方法都返回一个新实例,但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和a引用同一个对象,当我在b上使用+=时,它实际上改变了b(a也看到了这种改变——毕竟,它引用的是同一个列表)。然而,在第二种情况下,当我执行b=b+[1,2,3]时,这将获取b正在引用的列表,并将其与新列表[1,2,3]连接起来。然后,它将连接的列表存储在当前名称空间中为b,而不考虑b之前是哪一行。这是因为在第一次操作中将一个新对象分配给
list1
,而在第二次操作中将分配给list2
的原始对象更改
如果使用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]
试问一下,list1和list2上的操作不都假设执行相同的作业吗?它不是已经做到了吗?区别在哪里?
>>> 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]