为什么在列表上使用乘法运算符会创建指针列表?

时间:2012-01-11 16:18:26

标签: python list pointers

>>> rows = [['']*5]*5
>>> rows
[['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', '']]
>>> rows[0][0] = 'x'

当然,我希望行成为:

[['x', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', '']]

相反,我得到:

[['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', '']]

似乎行列表的元素是指向同一个旧[''] * 5列表的指针。为什么它以这种方式工作,这是一个Python功能吗?

3 个答案:

答案 0 :(得分:17)

该行为并非特定于重复运算符(*)。例如,如果使用+连接两个列表,则行为是相同的:

In [1]: a = [[1]]

In [2]: b = a + a

In [3]: b
Out[3]: [[1], [1]]

In [4]: b[0][0] = 10

In [5]: b
Out[5]: [[10], [10]]

这与列表是对象,对象通过引用存储的事实有关。当你使用*等时,它是重复的引用,因此你看到的行为。

以下表明rows的所有元素具有相同的标识(即CPython中的内存地址):

In [6]: rows = [['']*5]*5

In [7]: for row in rows:
   ...:     print id(row)
   ...:     
   ...:     
15975992
15975992
15975992
15975992
15975992

以下内容与您的示例等效,只是它为行创建了五个不同的列表:

rows = [['']*5 for i in range(5)]

答案 1 :(得分:4)

名称,函数参数和容器具有引用语义这一事实是Python中一个非常基本的设计决策。它影响了Python在许多方面的工作方式,您只选择了其中一个方面。在许多情况下,引用语义更方便,而在其他情况下,副本会更方便。在Python中,您可以随时显式创建副本,或者在这种情况下,使用列表推导:

rows = [[''] * 5 for i in range(5)]

您可以设计一种具有不同语义的编程语言,并且有许多语言具有不同的语义,以及具有相似语义的语言。为什么做出这个决定有点难以回答 - 一种语言只需要有一些语义,你总能问为什么。你也可以问为什么Python是动态类型的,最后答案是这就是Guido在1989年决定的。

答案 2 :(得分:4)

你是正确的,Python正在“引擎盖下”使用指针,是的,这是一个功能。我不确定为什么他们这样做 - 我认为这是为了速度和减少内存使用。

顺便说一下,这个问题是为什么理解shallow copies and deep copies之间的区别至关重要。