10

我可能遗漏了关于 list extend 的预期行为的一些信息,但为什么会发生以下情况?

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(在调用 extend 之前)实际上并不相等,即使==运算符和repr两者都会使它看起来好像它们是相同的。

我只是遇到这个,因为我想预先填充一个在运行时确定大小的空列表列表,然后意识到它不像我想象的那样工作。我可以找到更好的方法来做同样的事情,但现在我很好奇为什么这不起作用。这是 Python 2.5.2 BTW - 我没有安装更新的版本,所以如果这是一个错误,我不确定它是否已经修复。

4

3 回答 3

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指向同一个对象,但由于它们是不可变的,您不能更改int包含“1”的对象的值,只能重新分配元素。

这样做:a[1] = 5 将导致a显示为[0, 5, 0, 1].

于 2010-02-16T21:21:22.760 回答
4

该语句y = [[]] * 2绑定y到包含同一列表的 2 个副本的列表。采用:

y = [[], []]

或者

y = [[] for n in range(2)]
于 2010-02-16T21:20:19.937 回答
1

y包含对单个可变列表的两个引用。

于 2010-02-16T21:20:42.777 回答