如何在Python中扩展列表的列表?

10 投票
3 回答
3631 浏览
提问于 2025-04-15 19:22

我可能对列表扩展的预期行为有些误解,但为什么会发生以下情况呢?

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 个回答

1

y 里面有两个指向同一个可以改变的列表的引用。

4

这行代码 y = [[]] * 2 的意思是把 y 绑定到一个列表,这个列表里面有两个相同的空列表。你可以使用:

y = [[], []]

或者

y = [[] for n in range(2)]
17

[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]

撰写回答