我遇到了一个奇怪的行为,np.ndarray.tobytes()这让我怀疑它是否确定性地工作,至少对于dtype=object.
import numpy as np
print(np.array([1,[2]]).dtype)
# => object
print(np.array([1,[2]]).tobytes())
# => b'0h\xa3\t\x01\x00\x00\x00H{!-\x01\x00\x00\x00'
print(np.array([1,[2]]).tobytes())
# => b'0h\xa3\t\x01\x00\x00\x00\x88\x9d)-\x01\x00\x00\x00'
Run Code Online (Sandbox Code Playgroud)
在示例代码中,[1, [2]]首先将混合 python 对象列表 ( ) 转换为 numpy 数组,然后使用tobytes().
为什么相同数据的重复实例化得到的字节表示不同?该文档仅说明它将 an 转换ndarray为原始 python 字节,但并未提及任何限制。到目前为止,我仅针对dtype=object. 数字数组总是产生相同的字节序列:
np.random.seed(42); print(np.random.rand(3).tobytes())
# b'\xecQ_\x1ew\xf8\xd7?T\xd6\xbbh@l\xee?Qg\x1e\x8f~l\xe7?'
np.random.seed(42); print(np.random.rand(3).tobytes())
# b'\xecQ_\x1ew\xf8\xd7?T\xd6\xbbh@l\xee?Qg\x1e\x8f~l\xe7?'
Run Code Online (Sandbox Code Playgroud)
我是否错过了有关 python/numpy 内存架构的基本知识?我在 Mac 上使用 numpy 1.17.2 版进行了测试。
上下文:我在尝试计算任意数据结构的哈希时遇到了这个问题。我希望我可以依靠 的基本序列化功能tobytes(),但这似乎是一个错误的前提。我知道这pickle是 python 中序列化的标准,但由于我不需要可移植性,而且我的数据结构只包含数字,我首先寻求 numpy 的帮助。
数据类型数组object存储指向其包含的对象的指针。在 CPython 中,这对应于id. 每次创建新列表时,都会将其分配到新的内存地址。然而,小整数是被保留的,因此1每次都会引用相同的整数对象。
您可以通过检查一些示例对象的 ID 来准确了解其工作原理:
>>> x = np.array([1, [2]])
>>> x.tobytes()
b'\x90\x91\x04a\xfb\x7f\x00\x00\xc8[J\xaa+\x02\x00\x00'
>>> id(x[0])
140717641208208
>>> id(1) # Small integers are interned
140717641208208
>>> id(x[0]).to_bytes(8, 'little') # Checks out as the first 8 bytes
b'\x90\x91\x04a\xfb\x7f\x00\x00'
>>> id(x[1]).to_bytes(8, 'little') # Checks out as the last 8 bytes
b'\xc8[J\xaa+\x02\x00\x00'
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它具有相当的确定性,但序列化的信息对您来说基本上无用。数值数组的操作与对象数组的操作相同:它返回底层缓冲区的视图或副本。缓冲区的内容会让你失望。
既然您提到您正在计算哈希值,请记住,Python 列表不可哈希是有原因的。您可以拥有一次相同而另一次不同的列表。对于有效的哈希来说,使用 ID 通常不是一个好主意。