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)上提供相同的结果?
如文档中所述,内置的hash()函数不是为在外部某处存储结果哈希而设计的.它用于提供对象的哈希值,将它们存储在词典中等等.它也是特定于实现的(GAE使用Python的修改版本).查看:
>>> class Foo: ... pass ... >>> a = Foo() >>> b = Foo() >>> hash(a), hash(b) (-1210747828, -1210747892)
如您所见,它们是不同的,因为hash()使用object的__hash__
方法而不是'normal'哈希算法,例如SHA.
鉴于上述情况,理性选择是使用hashlib模块.
使用hashlib作为hash()
被设计用于:
在字典查找期间快速比较字典键
因此不保证它在Python实现中是相同的.
事实上,回应绝对不足为奇
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中所示.
大多数答案表明这是因为不同的平台,但还有更多.来自以下文件object.__hash__(self)
:
默认情况下,该
__hash__()
值str
,bytes
并且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
哈希结果在32位和64位平台之间变化
如果计算的散列在两个平台上应相同,请考虑使用
def hash32(value): return hash(value) & 0xffffffff
猜测,AppEngine正在使用64位的Python实现(-5768830964305142685不适合32位),你的Python实现是32位.您不能依赖于不同实现之间具有可比性的对象哈希值.
这是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
标志位怎么样?
例如:
十六进制值0xADFE74A5
表示未签名2919134373
和签名-1375832923
.必须对Currect值进行签名(符号位= 1),但python将其转换为无符号,并且在从64位转换为32位后,我们的哈希值不正确.
小心使用:
def hash32(value): return hash(value) & 0xffffffff