在for循环中Python的原地对象更新

3

Python 3.5及以上版本中:

x = np.zeros((2,3))
for x_e in x:
    x_e += 123

这个操作返回一个所有元素都是 1232x3 矩阵。而下面的操作则返回全是零的矩阵:

x = np.zeros((2,3))
for x_e in x:
    x_e = 123

对我来说,这有点令人不安,因为x_e是从x中取出的元素,而且它并不完全感觉到x正在被更新。

好的,我想这是一件事情,它被称为“原地”更新?(类似于原地算法?)

但是,让人感到不适的是,这在列表中不起作用:

x = [0,0,0]
for x_e in x:
    x_e += 123

这将返回列表。
[0, 0, 0]

我希望有人能为我解释这里到底发生了什么。


可能的重复问题:https://dev59.com/jFgR5IYBdhLWcg3wV8He,https://dev59.com/S2Up5IYBdhLWcg3wS2LP - Divakar
@Divakar,感谢您的指引。但我认为它们并没有回答关于整数不可变性的第三个问题。而且相信我,在我搜索互联网寻找答案时,我也找不到它们。 :) - Cem S.
2个回答

2
在第一个代码片段中,您对ndarray对象执行了一个原地加法操作。由于每个x_e都是一个ndarray,因此原位操作成功并更改了元素。
在第二个代码片段中,您仅重新分配给循环变量。没有对x的元素执行任何操作。
在第三个代码片段中,您没有多维列表,因此每个x_e实际上是一个int。在int上使用+=不会改变原址,它返回一个新的整数对象(您没有存储)。
以下可能更与第一个相关:
x = [[0, 0, 0] for _ in range(3)]
for x_e in x:
    x_e += [123]

这里的x中每个元素都是一个list对象[0, 0, 0],你需要就地添加元素123。执行后,x将变为:

[[0, 0, 0, 123], [0, 0, 0, 123], [0, 0, 0, 123]]

谢谢你的回答,这正是我想要澄清的。但是,我可以问一下为什么我们不能就地更改 int 吗? - Cem S.
@CemS。因为int对象是不可变的,语言设计者早在很久以前就做出了这个决定,因为它有许多好处。 - Dimitris Fasarakis Hilliard
@Kasramvd,谢谢!这让问题变得清晰明了。 - Cem S.

1

想象一下你有这样的代码:

>>> x = np.array(range(5))
>>> x
array([0, 1, 2, 3, 4])

所以现在:
>>> x+123
array([123, 124, 125, 126, 127])

正如您所看到的,'+'操作被映射到数组上。因此,当您创建一个由0填充的数组并将123添加到子列表时,得到的结果是合乎逻辑的。

是啊,但是为什么原地循环操作不能用于列表呢?对我来说新的是for循环中的原地操作。我理解广播,因为我来自Matlab。 - Cem S.
@CemS。这是因为您在for循环中更改了可变对象,该对象是主列表的子列表,并更改了可变对象,该对象是对原始对象的引用,对其进行的任何更改都将影响原始对象。 - Mazdak
@CemS.,是的,在Python中,列表和NumPy数组不是同一回事。例如,列表相对于数组的另一个重大区别是,内置函数(如sum)在列表中比数组中执行得快得多。我们只是用np.array来处理大数据。 - coder
在这种情况下,它们是相同的,这只是可变性而不是它们的功能。 - Mazdak
@Kasramvd,是的,我同意。我只是想澄清一下,通常情况下,列表和NumPy数组并不总是相同的东西。 - coder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接