当我运行 www.example.com ,我在运行之间得到了不同的b
的哈希值,而a
的哈希值保持不变。
script.py
from sympy import Symbol
import numpy as np
x = Symbol("x")
a = np.array([ 1, x])
b = np.array([1.0, x])
print(hash(a.tobytes()))
print(hash(b.tobytes()))
运行此脚本将产生以下输出。
In [1]: runfile(./script.py)
-1258340495102975319
3795610135772286033
In [2]: runfile(./script.py)
-1258340495102975319
7432739601143179777
In [3]: runfile(./script.py)
-1258340495102975319
1451381667883822748
In [4]: runfile(./script.py)
-1258340495102975319
2683979045255549228
In [5]: runfile(./script.py)
-1258340495102975319
-345973347917904018
请有人能对这种奇怪的行为给出一些解释,并可能提出一个解决方案,为浮点情况给予一致的哈希。
我尝试在for循环中模拟相同的行为,但在这种情况下,哈希值是一致的。当代码运行多次时,就会出现差异。
我不希望a
和b
的哈希值相同,因为1
和1.0
是不同的数据类型,但我希望两者的哈希值在运行过程中保持一致(就像a
一样)。
这个问题并不是散列所特有的,tobytes()方法在每次运行时都会给出不同的结果,但散列给出了更明显的差异表示。
编辑:经过更多的测试,我意识到这个问题不仅仅是SymPy特有的,同样的行为也发生在具有对象数据类型的NumPy数组上。例如,print(hash(np.array([1.0], dtype="O").tobytes()))
在不同的运行中给出不同的结果。
EDIT2:给出指针的答案仍然有一些无法解释的行为,因为这种行为只特定于对象数据类型的数组。
In [2]: hash(np.array([1.0]).tobytes())
Out[2]: -1405879698645296540
In [3]: hash(np.array([1.0]).tobytes())
Out[3]: -1405879698645296540
In [4]: hash(np.array([1.0]).tobytes())
Out[4]: -1405879698645296540
In [5]: hash(np.array([1.0]).tobytes())
Out[5]: -1405879698645296540
In [6]: hash(np.array([1.0], dtype="O").tobytes())
Out[6]: 7075328050134915067
In [7]: hash(np.array([1.0], dtype="O").tobytes())
Out[7]: -6443853770133964536
In [8]: hash(np.array([1.0], dtype="O").tobytes())
Out[8]: 889083274033361878
In [9]: hash(np.array([1.0], dtype="O").tobytes())
Out[9]: -6819397306369441685
1条答案
按热度按时间im9ewurl1#
第1条:Numpy数组只能保存数值。当你把一个
syms
,或任何其他非数值的对象放入一个数组中,即使是None
,数组的dtype将是object
,值将是指针。第2条:数组的哈希值,或者实际上任何正常对象,都取决于值。这意味着具有相同dtype和数值的两个不同数组应该具有相同的散列。
第3项:与ID不同,ID通常在每次运行中被不同地加盐,散列在运行之间被一致地计算。例如,CPython中
int
的哈希值就是int
的值。float
的散列在运行之间也是一致的。您正在创建一对指针数组。数组中
1
的值是一个指向解释器创建的内部int
对象的指针。x
也是指针。似乎发生的情况是,第一个数组的元素在脚本运行之间被分配在相同的位置,而float
没有。如果你有int
s或float
s的数组,使用非指针数据类型,数据将被直接散列,所有的散列都是相同的。要打印数组中的指针值,可以使用一个技巧绕过保护numpy put。
a.view(np.uint64)
和a.astype(np.uint64)
由于这些保护措施而无法工作。通过打印原始数组值(指针),您应该能够确认哪些对象被分配在与前一次运行相同的位置。