乘法运算符应用于列表(数据结构)
我正在阅读如何像计算机科学家一样思考,这是“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
会导致 ...每一行都是其他行的别名...< /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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
python 中的一切都是对象,除非明确要求,否则 python 永远不会复制。
当您
创建一个包含 10 个元素的列表时,所有元素都引用同一个
int
对象0
。由于整数对象是不可变,因此当您这样做时,
您正在更改列表的第二个元素,以便它引用另一个整数
15
。 由于int
对象的不变性,这总是有效的。这就是为什么
会创建一个包含 5 个元素的
list
对象,每个元素都是对与上面相同的innerList
的引用。 但由于list
对象是可变:与以下内容相同:
因为它们是对同一
list
对象的两个引用。 因此该元素最终会出现在该单个列表
中。 它看起来是重复的,但事实是只有一个list
对象,并且有许多对它的引用。相比之下,如果您
这样做,那么您将创建另一个
list
对象(将+
与列表一起使用是显式副本) ,并将对其的引用分配到outerList
的第二个位置。 如果您以这种方式“追加”元素(不是真正追加,而是创建另一个列表),innerList
将不受影响。EVERYTHING in python are objects, and python never makes copies unless explicity asked to do so.
When you do
you create a list with 10 elements, all of them refering to the same
int
object0
.Since integer objects are immutable, when you do
You are changing the second element of the list so that it refers to another integer
15
. That always works because ofint
objects immutability.That's why
Will create a
list
object with 5 elements, each one is a reference to the sameinnerList
just as above. But sincelist
objects are mutable:Is the same as:
Because they are two references to the same
list
object. So the element ends up in that singlelist
. It appears to be duplicated, but the fact is that there is only onelist
object, and many references to it.By contrast if you do
Here you are creating another
list
object (using+
with lists is an explicit copy), and assigning a reference to it into the second position ofouterList
. If you "append" the element this way (not really appending, but creating another list),innerList
will be unaffected.列表不是基元,它们是通过引用传递的。 列表的副本是指向列表的指针(用 C 术语来说)。 您对列表所做的任何操作都会发生在列表的所有副本及其内容的副本上,除非您执行浅复制。
哎呀,我们刚刚制作了一个指向 [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.
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.