【问题标题】:Multiply operator applied to list(data structure)应用于列表的乘法运算符(数据结构)
【发布时间】:2010-11-01 18:35:56
【问题描述】:

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

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

考虑函数 ma​​ke_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]]

ma​​ke_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

ma​​ke_matrix 的第一个版本失败(如书中 9.8 中所述)的原因是

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

不知道为什么

[[0] * columns] * rows

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

但不是

[[0] * columns]

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

【问题讨论】:

    标签: python list multiplication shallow-copy python-datamodel


    【解决方案1】:

    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 将不受影响。

    【讨论】:

    • 这只是告诉人们为什么错误的事情就是错误的事情,它并没有提供一个好的解决方案。解决方案:outerList = [[0]*10 for _ in range(5)] 是 Pythonic,紧凑,不会生成浅拷贝。
    【解决方案2】:

    列表不是原始的,它们是通过引用传递的。列表的副本是指向列表的指针(在 C 行话中)。您对列表所做的任何事情都会发生在列表的所有副本及其内容的副本上,除非您进行浅拷贝。

    [[0] * columns] * rows
    

    糟糕,我们刚刚创建了一个指向 [0] 的大指针列表。改变一个,你就改变了他们。

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

    【讨论】:

    • aha,对于大小为 1 的列表,这似乎不是一种特殊的行为。我听说“Pythonista”不喜欢特殊情况(如 Python 禅宗中所述“.. .特殊情况不足以打破规则......”)。
    • 误导。 python中没有“原始”之类的东西。一切都是一个对象,并且一直通过引用传递,包括整数。事实上变量只是命名的引用。这里的问题是列表是可变的,而整数不是。
    • 示例:a = 5 ; b = 一个; print a is b # 返回 True 因为 a 和 b 是对同一个对象的引用。
    猜你喜欢
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-04
    • 2013-09-18
    相关资源
    最近更新 更多