如何在Python中扩展列表的列表?
我可能对列表扩展的预期行为有些误解,但为什么会发生以下情况呢?
x = [[],[]]
y = [[]] * 2
print x # [[],[]]
print y # [[],[]]
print x == y # True
x[0].extend([1])
y[0].extend([1])
print x # [[1],[]], which is what I'd expect
print y # [[1],[1]], wtf?
我猜这里的*
运算符可能在做一些意想不到的事情,不过我也不太确定具体是什么。看起来在背后发生了一些事情,让原来的x和y(在调用扩展之前)实际上并不相等,尽管==
运算符和repr
都让人觉得它们是一样的。
我之所以发现这个问题,是因为我想要创建一个大小在运行时决定的空列表的列表,然后发现它并没有像我想的那样工作。我可以找到更好的方法来实现同样的功能,但现在我很好奇为什么这个方法不奏效。顺便说一下,这是Python 2.5.2版本——我没有安装更新的版本,所以如果这是个bug,我不确定它是否已经修复。
3 个回答
y
里面有两个指向同一个可以改变的列表的引用。
这行代码 y = [[]] * 2
的意思是把 y
绑定到一个列表,这个列表里面有两个相同的空列表。你可以使用:
y = [[], []]
或者
y = [[] for n in range(2)]
在 [something] * 2
这种情况下,Python 只是做了一个引用复制。所以,如果里面的类型是可变的,修改它们会在任何引用这个项目的地方都能看到变化。
在你的例子中,y[0]
和 y[1]
指向的是同一个列表对象。你可以通过 y[0] is y[1]
或者 id(y[0]) == id(y[1])
来验证这一点。
不过,你可以重新赋值列表中的元素,所以如果你这样做:
y[0] = [1]
你就把第一个元素重新绑定到了一个新的列表,这个列表里只有元素 "1",这样你就得到了你想要的结果。
在 Python 中,容器存储的是引用,很多序列容器都可以多次引用同一个项目。实际上,一个列表可以把自己作为元素引用,虽然这样做的用处有限。
如果你乘的是一个包含不可变类型的列表,这个问题就不会出现了:
a = [0, 1] * 2
上面的代码会给你一个列表 [0, 1, 0, 1]
,确实两个 1
指向的是同一个对象,但因为它们是不可变的,你不能改变这个包含 "1" 的 int
对象的值,只能重新赋值元素。
所以,执行 a[1] = 5
后,a
的值会变成 [0, 5, 0, 1]
。