【问题标题】:weird behaviour of numpy array element identitiesnumpy数组元素标识的奇怪行为
【发布时间】:2019-09-14 13:28:00
【问题描述】:

我用 python 列表编写了以下代码

# python lists
vc = [1,2,3,4]
print('original array')
print(hex(id(vc)))
print([hex(id(vc[i])) for i in range(len(vc))])
print(vc)
# --
g = vc[1:3]
print('array slice')
print(hex(id(g)))
print([hex(id(g[i])) for i in range(len(g))])
print(g)
# --
g[:] = [-1,-2]
print('original array')
print(hex(id(vc)))
print([hex(id(vc[i])) for i in range(len(vc))])
print(vc)
# --
print('array slice')
print(hex(id(g)))
print([hex(id(g[i])) for i in range(len(g))])
print(g)

产生预期的输出

original array
0x211acca9d48
['0x7ffc4ffbb350', '0x7ffc4ffbb370', '0x7ffc4ffbb390', '0x7ffc4ffbb3b0']
[1, 2, 3, 4]
array slice
0x211acc69e88
['0x7ffc4ffbb370', '0x7ffc4ffbb390']
[2, 3]
original array
0x211acca9d48
['0x7ffc4ffbb350', '0x7ffc4ffbb370', '0x7ffc4ffbb390', '0x7ffc4ffbb3b0']
[1, 2, 3, 4]
array slice
0x211acc69e88
['0x7ffc4ffbb310', '0x7ffc4ffbb2f0']
[-1, -2]

我们可以看到 python 列表切片创建了一个副本。一旦新数组 g 被修改,那么新数组的元素就会改变 ids。

如果我们对 numpy 数组重复同样的操作

# numpy arrays
import numpy as np
vc = np.array([1,2,3,4])
print('original array')
print(hex(id(vc)))
print([hex(id(vc[i])) for i in range(len(vc))])
print(vc)
# --
g = vc[1:3]
print('array slice')
print(hex(id(g)))
print([hex(id(g[i])) for i in range(len(g))])
print(g)
# --
g[:] = [-1,-2]
print('original array')
print(hex(id(vc)))
print([hex(id(vc[i])) for i in range(len(vc))])
print(vc)
# --
print('array slice')
print(hex(id(g)))
print([hex(id(g[i])) for i in range(len(g))])
print(g)

我们得到输出

original array
0x211acbe64e0
['0x211acd107e0', '0x211acd107e0', '0x211acd107e0', '0x211acd107e0']
[1 2 3 4]
array slice
0x211acd674e0
['0x211acd107e0', '0x211acd107e0']
[2 3]
original array
0x211acbe64e0
['0x211acd107e0', '0x211acd107e0', '0x211acd107e0', '0x211acd107e0']
[ 1 -1 -2  4]
array slice
0x211acd674e0
['0x211acd107e0', '0x211acd107e0']
[-1 -2]

我们看到 numpy 数组的切片会产生视图,但元素 id 没有任何意义。我正在考虑使用 ids 作为一种方法来了解何时使用 numpy(和 pandas)复制内容以及何时创建视图,但我无法理解发生了什么。

【问题讨论】:

  • id 在查看numpy 操作时毫无用处。
  • @hpaulj 非常感谢您的回答和 ilnk。 np.shares_memory 确实非常有用,因为它可以轻松检查何时创建视图以及何时创建副本。

标签: python numpy


【解决方案1】:

列表和数组之间的一个区别是列表存储 python 对象,而数组存储原始数据。因此,在检索单个元素时,列表 __getitem__ 可以简单地返回一个引用,而数组 __getitem__ 必须首先从原始数据创建一个 python 对象。

在当前的 cpython 实现中,id 返回一个对象的内存地址。由于__getitem__ 创建的数组元素对象在离开作用域后立即被释放,底层内存被回收,这就是所有元素具有相同 id 的原因。

您可以通过保持新生成的对象活动(通过引用它们)来检查这一点,在这种情况下将生成新的 id。即使您多次检索相同的元素:

repeat = [g[0] for dummy in "123"]
repeat
# [-1, -1, -1]
print([hex(id(x)) for x in repeat])
# ['0x7f2961d56f60', '0x7f2961d56f78', '0x7f2961d56f48']

【讨论】:

    【解决方案2】:

    我对 numpy 很陌生,但我在文档中发现了这一点: “NumPy 切片创建一个视图,而不是像字符串、元组和列表等内置 Python 序列的情况下的副本。” 因此,当您使用 numpy 数组切片时,您会从原始数组中获取元素的指针——因此 ids 不会改变(与原始数组中的指针相同)。 在修改列表后的列表中,由于创建了指向元素的新指针,因此 id 确实发生了变化。

    但我不确定我的回答是否准确。

    【讨论】:

      猜你喜欢
      • 2015-08-16
      • 2016-05-22
      • 1970-01-01
      • 2020-01-14
      • 1970-01-01
      • 1970-01-01
      • 2013-09-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多