变量克隆行为不明确

Unclarity on variable cloning behavior

除了一本书,我还得到了一个 Python 程序,我现在正在深入研究它。

程序使用了一个名为globdat的全局数据结构,在具体例程中将globdat内的一个numpy数组赋值给一个局部变量:

a = globdat.array

然后在接下来的 while 循环中,变量 a 每次迭代都会根据以下条件更新:

a[:] += da[:]

本次操作的结果是globdat.array被更新,用于后续操作。

这里需要使用[:],还是仅仅用来表示它也克隆到globdat.array?谁能阐明这种编码风格?

这个说法比较恶心:

a[:] += da[:]

翻译成这样:

a.__setitem__(slice(None),
              a.__getitem__(slice(None)).__iadd__(da.__getitem__(slice(None))))

这会产生不必要的两个列表副本。

假设 ada 是列表,你可以更合理地使用 extend() 方法:

a.extend(da)

如果您想就地修改列表,而不是用新列表替换它,您需要使用切片语法。

a[:] = da[:]

虽然在这种情况下,+=总是 修改列表,因此切片是多余的。

这可能是 cargo cult programming 的完美示例。

右侧的第二个 [:] 是多余的。它只是在串联使用之前复制 da,这是毫无意义的。

我们还剩下:

a[:] += da

首先,让我们了解一下 a += da 的作用。它映射到:

a = a.__iadd__(da)

__iadd__ 的调用扩展了原始列表 a 和 returns self,即对列表的引用。之后发生的分配在这种情况下无效(与 a=a 相同)。

这样就达到了最初的目的,即扩展全局数组。


现在,a[:] += da 是做什么的?它映射到:

a[:] = a[:].__iadd__(da)

或更乏味地:

a.__setitem__(slice(None), a.__getitem__(slice(None)).__iadd__(da))

为了可读性,我们把它写成(不是有效的 python 语法):

a.__setitem__(:, a.__getitem__(:).__iadd__(da))

所以a[:].__iadd__(da):

  1. 创建 a 的副本(调用是 a2
  2. da 连接到 a2
  3. return a2

然后赋值a[:] = ...:

  1. a 中的所有值替换为 a2 中的所有值。

这样也达到了最初的目标,但是效率很低。


this question.

的答案中有一些关于这个东西的有趣细节