变量克隆行为不明确
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))))
这会产生不必要的两个列表副本。
假设 a
和 da
是列表,你可以更合理地使用 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)
:
- 创建
a
的副本(调用是 a2
)
- 将
da
连接到 a2
- return
a2
然后赋值a[:] = ...
:
- 将
a
中的所有值替换为 a2
中的所有值。
这样也达到了最初的目标,但是效率很低。
this question.
的答案中有一些关于这个东西的有趣细节
除了一本书,我还得到了一个 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))))
这会产生不必要的两个列表副本。
假设 a
和 da
是列表,你可以更合理地使用 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)
:
- 创建
a
的副本(调用是a2
) - 将
da
连接到a2
- return
a2
然后赋值a[:] = ...
:
- 将
a
中的所有值替换为a2
中的所有值。
这样也达到了最初的目标,但是效率很低。
this question.
的答案中有一些关于这个东西的有趣细节