为什么在Android实现上运行Python哈希函数时结果不同?

28

我曾认为hash()函数在所有Python解释器中都是相同的。但是,当我在我的手机上使用Python for Android运行它时,它与其他Python解释器有所不同。对于字符串和数字类型的哈希值,我得到了相同的结果,但是对于内置数据类型的哈希值却不同。

PC Python解释器(Python 2.7.3)

>>> hash(int)
31585118
>>> hash("hello sl4a")
1532079858
>>> hash(101)
101

移动Python解释器(Python 2.6.2)

>>> hash(int)
-2146549248
>>> hash("hello sl4a")
1532079858
>>> hash(101)
101

有人能告诉我这是一个 bug 还是我理解错了什么吗?


我不知道为什么hash()的结果会有所不同,但也许你可以使用base64代替:http://docs.python.org/2/library/base64.html - gitaarik
@rednaw 谢谢,但我只是想知道拥有不同的哈希值是否正常。 - Balakrishnan
7
不应依赖于哈希值在不同的解释器之间保持恒定,规范中没有保证这种行为。唯一的保证是在特定解释器的特定运行中,哈希值将始终保持相同。 - mgilson
这是 https://dev59.com/rXRA5IYBdhLWcg3w4SDo 的重复吗? - David Cary
5个回答

39

hash()默认情况下在每次启动新实例时(Python3.3+),会进行随机化以防止字典插入DOS攻击

在此之前,hash()在32位和64位版本中是不同的.

如果您想要每次都得到相同的哈希值,请使用 hashlib 中的一个哈希函数。

>>> import hashlib
>>> hashlib.algorithms
('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')

对于字符串...我不确定他们是否将随机化添加到整数或其他内置类型中...(但我也要提出这一点) - mgilson
2
但是,没有哈希数据类型的哈希算法。 - Balakrishnan
你如何将它们转换为字符串?也许使用pickle - John La Rooy
hashlib中的哈希函数是加密的,由于性能原因,在所有情况下都不是一个好的解决方案。 - martinkunev

10

对于旧版本的Python(至少是我的Python 2.7),似乎

hash(<some type>) = id(<type>) / 16

而对于CPython,id()是内存中的地址 - http://docs.python.org/2/library/functions.html#id

>>> id(int) / hash(int)                                                     
16                                                                              
>>> id(int) % hash(int)                                                 
0                                                                               

所以我的猜测是Android端口对于内存地址有一些奇怪的规定?

无论如何,鉴于上面的情况,类型的哈希值(还有其他内置对象我猜)将因为函数在不同的地址而不同。

相比之下,数值的哈希值(我认为你所说的“非内部对象”)(在添加随机内容之前)是根据它们的数值计算出来的,因此可能是可重复的。

PS 但至少还有一个CPython细节:

>>> for i in range(-1000,1000):
...     if hash(i) != i: print(i)
...
-1

这里应该有一个答案解释那个问题...


是的,我同意你的观点,但是哈希如何处理非内部对象呢? - Balakrishnan
在Android中,>>> id(int) / hash(int)会返回-2L,而>>> id(int) % hash(int)会返回-2144680448L。 - Balakrishnan
1
@andrewcooke 哇,hash(-1)=-2 真的让我很烦恼。如果有人想知道相关问题,请看这里:https://dev59.com/G2kw5IYBdhLWcg3wHW3n - JeD

1

像int这样的东西的哈希依赖于id(),在运行之间或解释器之间不能保证恒定。也就是说,hash(int)将始终在程序运行期间产生相同的结果,但可能在不同的平台上或同一平台的不同运行中不相等。

顺便说一句,虽然Python中提供了哈希随机化功能,但默认情况下已禁用。由于您的字符串和数字哈希相等,很明显这不是问题所在。


5
Python旧版本默认情况下禁用哈希随机化。对于Python 3.3及更高版本,默认情况下启用哈希随机化。 - Duncan
@Sneftel 为什么Python返回相同的哈希值,即使对象具有相同的值但不同的ID?我期望 x='hello world'y='hello world' 具有不同的ID,因此它们应该具有不同的哈希值。 - ado sar
1
“只需对ID进行哈希处理”是hash函数的默认行为,适用于像Objecttype这样的东西,因为没有更普遍有用的默认行为。对于那些可以以合理方式哈希其值的东西(如字符串和整数),它会哈希该值而不是标识。 - Sneftel
记住,通过值哈希字符串是您可以可靠地执行 mymap["foo"] = 3 并期望稍后访问 mymap["foo"] 值的唯一原因。 - Sneftel
在“mymap [“foo”] = 3”的情况下,为什么必须使用该值?例如,如果字典有一个键“foo”,则ID是唯一的,那么为什么必须使用该值呢?我的意思是,该键不是变量,可能稍后指向其他对象。 - ado sar
因为你在第10行使用的字面字符串“foo”不一定是与你在第20行使用的字面字符串“foo”相同的对象。如果字符串不是按值哈希的,那么你代码中的每个“foo”实例都可能具有不同的哈希值。 - Sneftel

1
使用CPython,为了提高效率,内部对象的hash()返回与id()相同的值,后者返回对象的内存位置(“地址”)。

从一个基于CPython的解释器到另一个解释器,这种对象的内存位置可能会发生变化。根据您的操作系统,这可能会在一次运行到另一次运行中发生变化。


这已经不再是真的了,请参见https://dev59.com/5Ggu5IYBdhLWcg3wbWk9。 - laike9m

0

从Python 3.3开始,默认哈希算法创建的哈希值会与一个随机值混合,即使在同一台计算机上的不同Python进程之间也是如此。

目前仅对字符串实现了哈希随机化 - 因为它被认为是最有可能从外部捕获并遭受攻击的数据类型。

相同的frozenset在不同的计算机甚至不同的进程中始终产生相同的哈希值。

来源: https://www.quora.com/Do-two-computers-produce-the-same-hash-for-identical-objects-in-Python


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接