Windows XP,Python 2.5:
hash('http://stackoverflow.com') Result: 1934711907
Google App Engine(http://shell.appspot.com/):
hash('http://stackoverflow.com') Result: -5768830964305142685
为什么?我怎样才能有一个哈希函数,它可以在不同平台(Windows,Linux,Mac)上提供相同的结果?
答案 0 :(得分:88)
如文档中所述,内置的hash()函数是而不是,用于在外部存储结果哈希值。它用于提供对象的哈希值,将它们存储在词典中等等。它也是特定于实现的(GAE使用Python的修改版本)。退房:
>>> class Foo:
... pass
...
>>> a = Foo()
>>> b = Foo()
>>> hash(a), hash(b)
(-1210747828, -1210747892)
正如您所看到的,它们是不同的,因为hash()使用对象的__hash__
方法而不是“普通”哈希算法,例如SHA。
鉴于上述情况,理性选择是使用hashlib模块。
答案 1 :(得分:55)
答案 2 :(得分:32)
回应绝对不足为奇:实际上
In [1]: -5768830964305142685L & 0xffffffff
Out[1]: 1934711907L
因此,如果您想在ASCII字符串上获得可靠的响应,只需将低32位作为uint
。字符串的哈希函数是32位安全的,几乎可移植。
另一方面,你完全不能依赖于获取未明确定义hash()
方法的任何对象的__hash__
为不变量。
通过ASCII字符串,它的工作原理只是因为哈希是在形成字符串的单个字符上计算的,如下所示:
class string:
def __hash__(self):
if not self:
return 0 # empty
value = ord(self[0]) << 7
for char in self:
value = c_mul(1000003, value) ^ ord(char)
value = value ^ len(self)
if value == -1:
value = -2
return value
其中c_mul
函数是C中的“循环”乘法(没有溢出)。
答案 3 :(得分:16)
大多数答案表明这是因为不同的平台,但还有更多。来自the documentation of object.__hash__(self)
:
默认情况下,
__hash__()
,str
和bytes
的{{1}}值datetime
个对象被“腌制”,具有不可预测的随机值。 虽然它们在单个Python进程中保持不变, 它们在重复调用Python之间是不可预测的。这旨在提供针对拒绝服务的保护 由精心挑选的输入引起的最坏情况 dict插入的性能,O(n²)复杂度。看到 http://www.ocert.org/advisories/ocert-2011-003.html了解详情。
更改哈希值会影响
dicts
,sets
的迭代顺序 和其他映射。 Python从未对此做过保证 排序(通常在32位和64位版本之间变化)。
即使在同一台机器上运行,也会在调用过程中产生不同的结果:
$ python -c "print(hash('http://stackoverflow.com'))"
-3455286212422042986
$ python -c "print(hash('http://stackoverflow.com'))"
-6940441840934557333
虽然:
$ python -c "print(hash((1,2,3)))"
2528502973977326415
$ python -c "print(hash((1,2,3)))"
2528502973977326415
另请参阅环境变量PYTHONHASHSEED
:
如果未将此变量设置或设置为
random
,则使用随机值 种子str
,bytes
和datetime
个对象的哈希值。如果将
PYTHONHASHSEED
设置为整数值,则将其用作固定值 用于生成哈希所涵盖类型的hash()
的种子 随机化。其目的是允许可重复的散列,例如用于自我测试 解释器本身,或允许一组python进程 共享哈希值。
整数必须是
[0, 4294967295]
范围内的十进制数。 指定值0
将禁用散列随机化。
例如:
$ export PYTHONHASHSEED=0
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
答案 4 :(得分:8)
哈希结果在32位和64位平台之间变化
如果计算的散列在两个平台上都相同,请考虑使用
def hash32(value):
return hash(value) & 0xffffffff
答案 5 :(得分:6)
据猜测,AppEngine正在使用64位的Python实现(-5768830964305142685不适合32位),而Python的实现是32位。您不能依赖于不同实现之间具有可比性的对象哈希值。
答案 6 :(得分:6)
这是Google在python 2.5的生产中使用的哈希函数:
def c_mul(a, b):
return eval(hex((long(a) * b) & (2**64 - 1))[:-1])
def py25hash(self):
if not self:
return 0 # empty
value = ord(self[0]) << 7
for char in self:
value = c_mul(1000003, value) ^ ord(char)
value = value ^ len(self)
if value == -1:
value = -2
if value >= 2**63:
value -= 2**64
return value
答案 7 :(得分:5)
标志位怎么样?
例如:
十六进制值0xADFE74A5
代表无符号2919134373
并签名{{1}}。
必须对Currect值进行签名(符号位= 1),但python将其转换为无符号,并且在从64位转换为32位后,我们的哈希值不正确。
小心使用:
-1375832923
答案 8 :(得分:3)
字符串的多项式哈希。 1000000009
和239
是任意素数。不太可能偶然发生碰撞。模块化算术不是很快,但是为了防止碰撞,这比以2
的模幂运算更可靠。当然,很容易发现碰撞。
mod=1000000009
def hash(s):
result=0
for c in s:
result = (result * 239 + ord(c)) % mod
return result % mod
答案 9 :(得分:2)
答案 10 :(得分:-3)
它可能只是询问操作系统提供的功能,而不是它自己的算法。
正如其他评论所说,使用hashlib或编写自己的哈希函数。