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

发布于 2024-07-24 04:31:30 字数 1330 浏览 5 评论 0原文

我正在阅读如何像计算机科学家一样思考,这是“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

会导致 ...每一行都是其他行的别名...< /em>

但不是

[[0] * columns]

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

I'm reading How to think like a computer scientist which is an introductory text for "Python Programming".

I want to clarify the behaviour of multiply operator (*) when applied to lists.

Consider the function 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

The actual output is

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

The correct version of make_matrix is :

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

The reason why first version of make_matrix fails ( as explained in the book at 9.8 ) is that

...each row is an alias of the other rows...

I wonder why

[[0] * columns] * rows

causes ...each row is an alias of the other rows...

but not

[[0] * columns]

i.e. why each [0] in a row is not an alias of other row element.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陌若浮生 2024-07-31 04:31:30

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 对象,并且有许多对它的引用。

相比之下,如果您

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

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

EVERYTHING in python are objects, and python never makes copies unless explicity asked to do so.

When you do

innerList = [0] * 10

you create a list with 10 elements, all of them refering to the same int object 0.

Since integer objects are immutable, when you do

innerList[1] = 15

You are changing the second element of the list so that it refers to another integer 15. That always works because of int objects immutability.

That's why

outerList = innerList * 5

Will create a list object with 5 elements, each one is a reference to the same innerList just as above. But since list objects are mutable:

outerList[2].append('something')

Is the same as:

innerList.append('something')

Because they are two references to the same list object. So the element ends up in that single list. It appears to be duplicated, but the fact is that there is only one list object, and many references to it.

By contrast if you do

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

Here you are creating another list object (using + with lists is an explicit copy), and assigning a reference to it into the second position of outerList. If you "append" the element this way (not really appending, but creating another list), innerList will be unaffected.

咆哮 2024-07-31 04:31:30

列表不是基元,它们是通过引用传递的。 列表的副本是指向列表的指针(用 C 术语来说)。 您对列表所做的任何操作都会发生在列表的所有副本及其内容的副本上,除非您执行浅复制。

[[0] * columns] * rows

哎呀,我们刚刚制作了一个指向 [0] 的指针的大列表。 改变一个,你就改变了所有。

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

lists are not primitives, they are passed by reference. A copy of a list is a pointer to a list (in C jargon). Anything you do to the list happens to all copies of the list and the copies of its contents unless you do a shallow copy.

[[0] * columns] * rows

Oops, we've just made a big list of pointers to [0]. Change one and you change them all.

Integers are not passed by reference, they are really copied, therefore [0] * contents is really making lots of NEW 0's and appending them to the list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文