比较两个NumPy数组的对象标识符

4

我已经使用numpy有一段时间了,但我遇到了一个我没有完全理解的问题:

a = np.ones(20)
b = np.zeros(10)

print(id(a)==id(b))          # prints False
print(id(a), id(b))          # prints (4591424976, 4590843504)

print(id(a[0])==id(b[0]))    # prints True 
print(id(a[0]), id(b[0]))    # prints (4588947064, 4588947064)

print(id(a[0]))              # 4588947184
print(id(b[0]))              # 4588947280

请问有人能解释一下最后四个打印语句中观察到的行为吗?同时,我知道id会给你分配在内存中实际的唯一对象标识符,但是每次运行最后两个打印语句时,我得到了不同的id值。这是预期的行为吗?


4
id(a[0])并没有提供有用的信息。 a既不是列表,也不是对象数据类型数组,a[0]是一个值,而不是数组中唯一的对象。换句话说,每次进行[0]索引时,numpy都会创建一个新的包含该值的np.float对象。 - hpaulj
3
@Chris,错误的重复。这不是关于Python整数和/或浮点数的“id”的问题,而是关于“numpy”数组元素的“拆箱”问题。 - hpaulj
1
@Chris 我认为这个完全不同。 - enterML
2个回答

9
简短回答是,你应该忘记依赖于id来深入了解Python的工作原理。它的输出受到CPython实现细节、Peephole优化和内存重用的影响。往往id是一个误导。这在numpy中尤其如此。
在您的特定情况下,只有ab存在为Python对象。当您获取一个元素a[0]时,您实例化一个新的Python对象,一个类型为numpy.float64的标量。这些都是新的Python对象,因此会被赋予新的id,除非解释器意识到您正在尝试两次使用此对象(这可能是发生在您的中间示例中的情况,尽管我发现两个具有不同值的numpy.float64对象被赋予相同的id令人惊讶。但如果您先将a[0]b[0]分配给适当的名称,奇怪的魔法就会消失,所以这可能是由于某种优化引起的)。也可能发生解释器重用内存地址,给您之前出现过的id

只是为了看看numpy中id是多么无意义,即使是微不足道的视图也是新的Python对象,具有新的id,尽管就所有目的而言,它们和原始对象一样好:

>>> arr = np.arange(3)

>>> id(arr)
140649669302992

>>> id(arr[...])
140649669667056

这里有一个与交互式 shell 中 id 重用相关的示例:

>>> id(np.arange(3))
139775926634896

>>> id(np.arange(3))
139775926672480

>>> id(np.arange(3))
139775926634896

当涉及到numpy数组时,显然不存在像int interning这样的事情,因此上述情况只是由于解释器重用id而导致的。id返回内存地址的事实再次只是CPython实现细节。忘记id。您可能想要在numpy中使用的唯一内容是numpy.may_share_memorynumpy.shares_memory

0

需要注意的是,在Python中所有东西都是对象,包括数字和类。 你已经有了两个numpy阵列对象,每个对象都包含相同的值,即0。 当你说:

print('id of 0 =',id(0))

a = 0
print('id of a =',id(a))

b = a
print('id of b =',id(b))

c = 0.0
print('id of c =',id(c))

你会得到类似以下的答案(但你的情况可能不同):
id of 0 = 140472391630016
id of a = 140472391630016
id of b = 140472391630016
id of c = 140472372786520

因此,整数0具有唯一的标识符。整数0的标识符在其生命周期内保持不变。浮点数0.0和其他对象也是如此。 因此,在您的情况下,a[0]b[0]的零对象ID将保持不变,直到它死亡,因为两者都包含0作为对象值。 每次在不同行中打印a[0]b[0]时,您都会返回其不同的对象标识,因为您在不同的行触发它,因此具有不同的生命周期。 您可以尝试:
print(id(a)==id(b))
print(id(a),id(b))
print(id(a[0])==id(b[0]))
print(id(a[0]),id(b[0]))

输出将是:

False
2566443478752 2566448028528
True
2566447961120 2566447961120

请注意第二行会返回两个不同的numpy数组对象的身份标识,因为它们都是不同的列表。

我不确定我理解你的观点。"整数0的标识在其生命周期内保持不变":这是正确的,但在a [0]中,该" 0"的生命周期仅限于该索引表达式。您在a [0],b [0]中获得相同的0对象是cpython实现细节。但对于a [1000],b [1000],这就不再成立了,但我认为这不会影响OP所询问的行为。 - Andras Deak -- Слава Україні
嗨Andras,我从https://www.programiz.com/python-programming/methods/built-in/id这个来源中找到了一些关于ID的细节。我只是为了解释ID在对象的生命周期内保持不变而提出了我的观点。我错过了并没有提及来自cpython角度的答案。 - MD Rijwan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接