【问题标题】:Unclarity on variable cloning behavior变量克隆行为不明确
【发布时间】:2015-05-07 14:44:40
【问题描述】:

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

程序使用名为globdat的全局数据结构,在特定例程中将globdat内的numpy数组分配给局部变量:

a = globdat.array

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

a[:] += da[:]

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

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

【问题讨论】:

  • globdat.array 是 python 列表还是 numpy 数组或其他什么?这很重要。
  • @StefanPochmann globdat.array 确实是一个 numpy 数组。如果这改变了所提供答案的相关性,我很抱歉。
  • 他们似乎对主要问题仍然是正确的,但我是一个麻木的新手,不能确定。我会问最佳答案的,因为我也有兴趣。

标签: python list python-2.7 operators in-place


【解决方案1】:

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

我们只剩下:

a[:] += da

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

a = a.__iadd__(da)

__iadd__ 的调用扩展了原始列表a,并返回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. 返回a2

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

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

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


this question 的回答中有一些关于这些东西的有趣细节。

【讨论】:

  • 你能判断切片是否对 numpy 数组(或数组和列表的混合)有意义吗? OP证实了我的怀疑并更新了问题,我也很好奇。
【解决方案2】:

这句话有点恶心:

a[:] += da[:]

翻译成这样:

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

这会造成两个列表的不必要副本。

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

a.extend(da)

【讨论】:

    【解决方案3】:

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

    a[:] = da[:]
    

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

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

    【讨论】:

      猜你喜欢
      • 2020-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-29
      • 2010-12-05
      • 2011-02-08
      相关资源
      最近更新 更多