内置python hash()函数

时间:2009-04-27 14:31:01

标签: python google-app-engine 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)上提供相同的结果?

11 个答案:

答案 0 :(得分:88)

如文档中所述,内置的hash()函数是而不是,用于在外部存储结果哈希值。它用于提供对象的哈希值,将它们存储在词典中等等。它也是特定于实现的(GAE使用Python的修改版本)。退房:

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

正如您所看到的,它们是不同的,因为hash()使用对象的__hash__方法而不是“普通”哈希算法,例如SHA。

鉴于上述情况,理性选择是使用hashlib模块。

答案 1 :(得分:55)

hashlib用作hash() was designed to be used to

  

在字典查找期间快速比较字典键

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

答案 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__()strbytes的{​​{1}}值   datetime个对象被“腌制”,具有不可预测的随机值。   虽然它们在单个Python进程中保持不变,   它们在重复调用Python之间是不可预测的。

     

这旨在提供针对拒绝服务的保护   由精心挑选的输入引起的最坏情况   dict插入的性能,O(n²)复杂度。看到   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

另请参阅环境变量PYTHONHASHSEED

  

如果未将此变量设置或设置为random,则使用随机值   种子strbytesdatetime个对象的哈希值。

     

如果将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)

字符串的多项式哈希。 1000000009239是任意素数。不太可能偶然发生碰撞。模块化算术不是很快,但是为了防止碰撞,这比以2的模幂运算更可靠。当然,很容易发现碰撞。

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

答案 9 :(得分:2)

PYTHONHASHSEED的值可能用于初始化哈希值。

尝试:

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

答案 10 :(得分:-3)

它可能只是询问操作系统提供的功能,而不是它自己的算法。

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