前言
在Python/ target=_blank class=infotextkey>Python中,有一個內(nèi)置函數(shù) hash(),它可以生成任何對象的哈希值,在進行對象不比較的時候,其實就是比較對象的哈希值
但是,你是否做過下面的操縱?
>>> iNFTy = float('inf')
>>> infty
inf
>>> type(infty)
<class 'float'>
>>> hash(infty)
314159
這里創(chuàng)建了一個表示無窮的浮點數(shù)對象infty,然后將它作為hash()函數(shù)的參數(shù),即得到無窮的哈希值,結(jié)果是31459,對這個結(jié)果的數(shù)字組成,應(yīng)該并不陌生吧。
>>> import math
>>> int(math.pi*1e5)
314159
它就是組成 的部分數(shù)字。為什么會是這個結(jié)果,這里有什么玄妙嗎?
沒有什么玄妙的,都是語言中的規(guī)定。不要忘記,Python語言是一種高級編程語言,編程語言都是人工語言,所以就會有很多人為的硬性規(guī)定。
回到hash()函數(shù),它是Python的一個內(nèi)置函數(shù),在上面的程序中調(diào)用它的時候,函數(shù)的指針由內(nèi)置float類型(PyTypeObject PyFloat_Type)的tp_hash屬性給出,即float_hash函數(shù),并且以return _Py_HashDouble(v-> ob_fval)定義返回值,實現(xiàn)返回值的代碼:
if (Py_IS_INFINITY(v))
return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
這里的_PyHASH_INF就定義為314159:
#define _PyHASH_INF 314159
所以,可以把hash(float('inf'))理解為系統(tǒng)的規(guī)定,或者,在Python3中,也可以說是sys.hash_info.inf的結(jié)果:
>>> import sys
>>> sys.hash_info
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
>>> sys.hash_info.inf
314159
這個結(jié)果,是有其歷史原因的。在2000年8月,Tim Peters提交了一個代碼,其中修復(fù)了當時Python的一個bug。內(nèi)容如下(
https://github.com/python/cpython/commit/39dce29365d287dc6b353b2a527dc11fe58dcfa6):
在這次提交的代碼中,Tim Peters 將 static long float_hash(PyFloatObject *v 從Objects/floatobject.c中剝離出來,并且實現(xiàn)下面的返回值:return _Py_HashDouble(v->ob_fval);。在Obbjects/obbject.c中的long _Py_HashDouble(double v)里面增加了下面的兩行:
if (Py_IS_INFINITY(intpart))
/* can't convert to long int -- arbitrary */
v = v < 0 ? -271828.0 : 314159.0;
由此可見,那個結(jié)果就是人為的規(guī)定。注意,上面代碼中還有另外一個數(shù)字,271828,就是 自然常數(shù) 所包含的幾個數(shù)字。
但是,如果在Python3中,負無窮的哈希值會是:
>>> hash(float('-inf'))
-314159
在Pyhton2中,結(jié)果就不同了:
>>> hash(float('-inf'))
-271828
后來還有一些細微的變化,可以參閱:
https://github.com/python/cpython/commit/985ecdcfc29adfc36ce2339acf03f819ad414869