python內建的hash函式對於字串來說,每次得到的值不一樣?Coldwings2017-03-23 23:40:29

set/dict的hash還真就是這個玩意實現的,因為它保證了在同一個直譯器程序裡相同字串hash一致。

因為CPython 3。x裡的str,它的實體是unicode物件,實體是個utf-8 bytes或者是wstr(嗯這裡真特麼有個『或者』),並且透過一個叫做unicodedata_db的玩意來實現快取(不然就沒法兒保證str物件的不可變與地址一致性了)。

於是乎當你調內部hash的時候,反正不同程序中的直譯器不會共用一個unicodedata_db,這個直譯器程序裡的字串的hash到另一個程序裡指不定連個字串都不是,所以在計算這個內部hash的時候加入了一個code_magic的玩意,同時也均攤了一把複雜度,省得這個db以及set/dict對特定資料表現出極差效能。再說了,誰也不會傻到拿個直譯器內部hash去做跨程序交換。

所以真需要做可重現可跨程序保持一致性的hash,請用hashlib。

python內建的hash函式對於字串來說,每次得到的值不一樣?冒泡2017-03-23 23:51:43

python的字串hash演算法並不是直接遍歷字串每個字元去計算hash,而是會有一個secret prefix和一個secret suffix,可以認為相當於是給字串加鹽後做hash,可以規避一些規律輸入的情況

顯然這個secret前後綴的值會直接影響計算結果,而且它有一個啟動時隨機生成的機制,只不過,在2。x版本中,這個機制預設是關閉的,前後綴每次啟動都設定為0,除非你改了相關環境變數來要求隨機,而在3。x中修改了預設行為,如果你不配置環境變數,則預設是隨機一個前後綴值,這樣每次啟動都會不同

這個環境變數是PYTHONHASHSEED,無論在2。x還是3。x中,配置為一個正整數,將作為隨機種子;配置為0,則secret前後綴預設清零(和2。x預設行為就一樣了),配置為空串或“random”,則表示讓程序隨機生成(和3。x預設行為一樣)

具體為啥要這麼做,猜測一個是為了安全性(防字串hash表的攻擊,比如php曾經碰到的攻擊),另一個可能也是強調不要依賴一些內建結果,因為這種演算法可能隨著版本而更新,避免有些使用者不看文件,誤以為是永遠不變的

python內建的hash函式對於字串來說,每次得到的值不一樣?RednaxelaFX2017-03-24 05:27:40

給題主放倆老新聞的傳送門:

Huge portions of the Web vulnerable to hashing denial-of-service attack

Denial of service via hash collisions

這就是Python的字串hash隨機化的源頭。

Java(Oracle JDK / OpenJDK)也曾經採用過類似的修補方式,像這樣來可選地初始化一個隨機數seed:

http://

hg。openjdk。java。net/jdk

7u/jdk7u/jdk/file/1afb03696eb7/src/share/classes/java/util/HashMap。java#l332

/**

* Initialize the hashing mask value。 We defer initialization until we

* really need it。

*/

final

boolean

initHashSeedAsNeeded

int

capacity

{

boolean

currentAltHashing

=

hashSeed

!=

0

boolean

useAltHashing

=

sun

misc

VM

isBooted

()

&&

capacity

>=

Holder

ALTERNATIVE_HASHING_THRESHOLD

);

boolean

switching

=

currentAltHashing

^

useAltHashing

if

switching

{

hashSeed

=

useAltHashing

sun

misc

Hashing

randomHashSeed

this

0

}

return

switching

}

後來在Oracle JDK8 / OpenJDK8裡改為在衝突鏈長的時候使用紅黑樹來降低鏈的長度,就沒有繼續使用這個alternative hashing了。

python內建的hash函式對於字串來說,每次得到的值不一樣?靈劍2017-03-25 11:03:53

新版本Python的一個功能,內建hash函式帶有隨機的magic。補充一下其他答案,這個功能有一定的安全性上的考慮,可以讓攻擊者難以預測內建的set或者dict的一些行為,但遠不足以承擔真正的密碼安全級別的hash的作用。傳遞set和dict到其他程序的時候,只會傳遞其中的值,而不會傳遞hash表結構,hash表是傳到之後重新建立起來的。

python內建的hash函式對於字串來說,每次得到的值不一樣?知乎使用者2021-01-04 23:58:22

特意糾正一下 雷小雷 的回覆

菜鳥教程原文:

“在

hash()

對物件使用時,所得的結果不僅和物件的內容有關,還和物件的

id()

,也就是記憶體地址有關。”

這個是錯誤的說法。

我驗證了一下,和記憶體地址無關, 同一個python程序內只要值是一樣的,hash的值就是一樣的

result

=

‘12345’

12345

def

show_hash

i

):

name

=

str

result

i

])

print

f

‘id of name is {id(name)} hash

{name}

: {hash(name)}’

p_list

=

[]

for

i

in

range

2

):

p

=

Process

target

=

show_hash

args

=

i

,))

p

start

()

time

sleep

2