Python numpy数组元素标识的奇怪行为

Python numpy数组元素标识的奇怪行为,python,numpy,Python,Numpy,我已经用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))

我已经用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后,新数组的元素将更改ID

如果我们对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没有意义。我曾考虑使用ID作为一种手段来了解何时使用numpy(以及pandas)复制内容以及何时创建视图,但我无法理解正在发生的事情。

我对numpy非常陌生,但我在文档中发现了这一点: NumPy切片创建一个视图,而不是像内置Python序列(如字符串、元组和列表)那样创建一个副本 因此,当使用numpy数组切片时,可以从原始数组中获取元素的指针,这样ID就不会改变(与原始数组中的指针相同)。 在修改列表后的列表中,ID确实会更改,因为创建了指向元素的新指针


但我不确定我的答案是否准确。我对numpy很陌生,但我在文档中发现: NumPy切片创建一个视图,而不是像内置Python序列(如字符串、元组和列表)那样创建一个副本 因此,当使用numpy数组切片时,可以从原始数组中获取元素的指针,这样ID就不会改变(与原始数组中的指针相同)。 在修改列表后的列表中,ID确实会更改,因为创建了指向元素的新指针


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

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

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

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

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

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

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

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

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

查看
numpy
操作时,
id
无效。另请参见@hpaulj。非常感谢您的回答和ilnk。np.shares\u内存确实非常有用,因为它允许轻松检查何时创建视图以及何时制作副本。
id
在查看
numpy
操作时是无用的。另请参见@hpaulj非常感谢您的回答和ilnk。np.u内存确实非常有用,因为它允许轻松检查何时创建视图以及何时制作副本。