内置的python hash()函数

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)相同的结果?

使用hashlib作为hash() 被devise用于 :

在字典查找过程中快速比较字典键

因此不能保证它在Python实现中是相同的。

正如文档中所述,内置的hash()函数并不是为了在外部存储结果散列devise的。 它用于提供对象的散列值,将它们存储在字典中等等。 它也是实现特定的(GAE使用Python的修改版本)。 查看:

 >>> class Foo: ... pass ... >>> a = Foo() >>> b = Foo() >>> hash(a), hash(b) (-1210747828, -1210747892) 

正如你所看到的,它们是不同的,因为hash()使用对象的__hash__方法而不是“正常的”散列algorithm,比如SHA。

鉴于以上情况,合理的select是使用hashlib模块。

事实上,这个回应绝对不会让人意外

 In [1]: -5768830964305142685L & 0xffffffff Out[1]: 1934711907L 

所以如果你想在ASCIIstring上得到可靠的响应,只需要将低32位作为uint 。 string的散列函数是32位安全的, 几乎是可移植的。

另一方面,你根本就不能依赖得到你没有明确定义__hash__方法的任何对象的hash()是不变的。

在ASCIIstring上它的工作原理就是因为散列是在构成string的单个字符上计算的,如下所示:

 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)的文档object.__hash__(self)

默认情况下, strbytesdatetime对象的__hash__()值是“salted”的,具有不可预知的随机值。 虽然它们在单独的Python过程中保持不变,但在重复调用Python之间却无法预测。

这是为了防止由于精心挑选的input引起的拒绝服务,这些input利用了字典插入的最坏情况性能O(n2)的复杂性。 有关详细信息,请参阅http://www.ocert.org/advisories/ocert-2011-003.html

更改散列值会影响dictssets和其他映射的迭代次序。 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 

另见环境variablesPYTHONHASHSEED

如果此variables未设置或设置为random ,则会使用随机值对strbytesdatetime对象的散列进行播种。

如果PYTHONHASHSEED设置为整数值,则将其用作用于生成由散列随机化覆盖的types的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正在使用Python的64位实现(-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 

什么标志位?

例如:

hex值0xADFE74A5表示无符号2919134373并签署了-1375832923 。 正确的值必须被签名(符号位= 1),但Python将其转换为无符号,并且我们有一个不正确的散列值从64位转换到32位。

小心使用:

 def hash32(value): return hash(value) & 0xffffffff 

string的多项式散列。 1000000009239是任意素数。 偶然不小心碰撞。 模块化算术不是很快,但是为了防止碰撞,这比以2为模的方式更可靠。 当然,故意碰撞很容易。

 mod=1000000009 def hash(s): result=0 for c in s: result = (result * 239 + ord(c)) % mod return result % mod 

PYTHONHASHSEED的值可以用来初始化散列值。

尝试:

 PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))' 

它可能只是提供操作系统提供的function,而不是自己的algorithm。

正如其他评论所说,使用hashlib或编写自己的散列函数。