乘法运算符应用于列表(数据结构)

时间:2009-06-10 11:06:42

标签: python python-datamodel

我正在阅读How to think like a computer scientist这是“Python编程”的介绍性文本。

我想澄清乘法运算符(*)在应用于列表时的行为。

考虑功能 make_matrix

def make_matrix(rows, columns):
"""
  >>> make_matrix(4, 2)
  [[0, 0], [0, 0], [0, 0], [0, 0]]
  >>> m = make_matrix(4, 2)
  >>> m[1][1] = 7
  >>> m
  [[0, 0], [0, 7], [0, 0], [0, 0]]
"""
return [[0] * columns] * rows

实际输出是

[[0, 7], [0, 7], [0, 7], [0, 7]]

make_matrix 的正确版本是:

def make_matrix(rows, columns):
"""
  >>> make_matrix(3, 5)
  [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
  >>> make_matrix(4, 2)
  [[0, 0], [0, 0], [0, 0], [0, 0]]
  >>> m = make_matrix(4, 2)
  >>> m[1][1] = 7
  >>> m
  [[0, 0], [0, 7], [0, 0], [0, 0]]
"""
matrix = []
for row in range(rows):
    matrix += [[0] * columns]
return matrix

第一版 make_matrix 失败的原因(如9.8中的书中所述)是

...每一行都是其他行的别名......

我想知道为什么

[[0] * columns] * rows

导致 ...每一行都是其他行的别名......

但不是

[[0] * columns]

即。为什么连续的每个[0]都不是其他行元素的别名。

2 个答案:

答案 0 :(得分:18)

python中的所有内容都是对象,除非明确要求,否则python永远不会复制。

当你这样做时

innerList = [0] * 10

您创建一个包含10个元素的列表,所有这些元素都引用相同的int对象 0

由于整数对象不可变,所以当你执行

innerList[1] = 15

您正在更改列表的第二个元素,以便它引用另一个整数 15 。这总是有效的,因为 int 对象不变性。

这就是为什么

outerList = innerList * 5

将创建一个包含5个元素的list对象,每个元素都是对相同innerList 的引用,如上所述。但由于list个对象可变

outerList[2].append('something')

与:

相同
innerList.append('something')

因为它们是相同list对象的两个引用。因此元素最终会出现在单list中。它似乎是重复的,但事实是只有一个list对象,并且很多引用它。

相比之下,如果你这样做

outerList[1] = outerList[1] + ['something']

此处您正在创建另一个 list对象(使用+,其中列表是一个显式副本),并将其引用分配到outerList的第二个位置。如果您以这种方式“追加”元素(不是真正附加,而是创建另一个列表),innerList将不受影响。

答案 1 :(得分:-3)

列表不是原语,它们是通过引用传递的。列表的副本是指向列表的指针(用C术语表示)。除非您进行浅层复制,否则您对列表所做的任何操作都会发生在列表的所有副本及其内容的副本中。

[[0] * columns] * rows

哎呀,我们刚刚列出了一个指向[0]的指针。改变一个你就改变它们。

整数不是通过引用传递的,它们实际上是被复制的,因此[0] *内容实际上会产生很多新的0并将它们附加到列表中。