組み込みのPythonhash()関数


82

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)で同じ結果が得られるハッシュ関数を使用するにはどうすればよいですか?


14
これは、あなたのwinxpが32ビットプラットフォームであり、googleが64ビットであるという事実によるものです
Tzury Bar Yochay 2011年

回答:


56

次の目的で使用するように設計されたhashlibhash() 使用します。

辞書検索中に辞書キーをすばやく比較する

したがって、Pythonの実装全体で同じになることを保証するものではありません。


5
hashlib暗号化されていない使用では、ハッシュ関数は少し遅くなりませんか?
Brandon Rhodes

8
Jenkins、Bernstein、FNV、MurmurHashなどの汎用ハッシュ関数と比較すると、実際には非常に低速です。独自のハッシュテーブルのような構造を作成する場合は、uthash.h uthash.sourceforge.net
lericson

45
ベンチマーク:hash95 ns、binascii.crc32570 ns、hashlib.md5.digest()1.42 us、murmur.string_hash234 ns
temoto 2012年

hashPythonセッションごとに、ランダムに生成された新しいソルト値を使用します。したがって、Pythonセッション間で変更されます。
ホブ

89

ドキュメントに記載されているように、組み込みのhash()関数は、結果のハッシュを外部のどこかに格納するように設計されていません。オブジェクトのハッシュ値を提供したり、辞書に保存したりするために使用されます。また、実装固有です(GAEはPythonの修正バージョンを使用します)。チェックアウト:

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

ご覧のとおり、hash()は__hash__SHAなどの「通常の」ハッシュアルゴリズムではなくオブジェクトのメソッドを使用するため、これらは異なります。

上記を考えると、合理的な選択は、hashlibモジュールを使用することです。


ありがとうございました!私はここに来て、なぜ同じオブジェクトに対して常に異なるハッシュ値を取得し、dict(等しいかどうかをチェックするのではなくハッシュ+タイプでインデックスを付ける)で予期しない動作を引き起こすのだろうかと考えました。hashlib.md5から独自のintハッシュを生成する簡単な方法は、int(hashlib.md5(repr(self)).hexdigest(), 16)self.__repr__オブジェクトが同一である場合に同一であると定義されていると仮定して)です。32バイトが長すぎる場合は、変換前に16進文字列をスライスすることで、もちろんサイズを小さくすることができます。
アランプラム

1
考え直して__repr__みると、十分に一意である場合は、dictが同じハッシュを持つ等しくないオブジェクトを混同しないため、str.__hash__(つまりhash(repr(self)))を使用できます。これは、明らかに、reprがアイデンティティを表すことができるほどオブジェクトが些細なものである場合にのみ機能します。
アランプラム

したがって、2つのオブジェクトabを使用した例では、hashlibモジュールを使用して、オブジェクトが同一であることを確認するにはどうすればよいでしょうか。
ギャレット


32

応答はまったく驚くべきことではありません:実際には

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

したがって、ASCII文字列で信頼できる応答を取得する場合は、下位32ビットをとして取得しuintます。文字列のハッシュ関数は32ビットセーフで、ほとんど移植可能です。

一方、メソッドを不変であるとhash()明示的に定義していないオブジェクトの取得にまったく依存することはできません__hash__

ASCII文字列では、次のように、文字列を形成する1文字でハッシュが計算されるために機能します。

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のように(オーバーフローのない)「循環」乗算です。


18

ほとんどの回答は、これはプラットフォームが異なるためであることを示唆していますが、それだけではありません。からのドキュメントobject.__hash__(self)

デフォルトでは__hash__()、、および オブジェクトの値はstr、予測できないランダムな値で「ソルト」されます。それらは個々のPythonプロセス内で一定のままですが、Pythonを繰り返し呼び出す間で予測することはできません。bytesdatetime

これは、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、ランダムな値を使用してstrbytesおよびdatetimeオブジェクトのハッシュをシードします。

PYTHONHASHSEEDが整数値に設定されている場合hash()、ハッシュランダム化の対象となるタイプのを生成するための固定シードとして使用されます。

その目的は、インタープリター自体のセルフテストなど、繰り返し可能なハッシュを許可すること、またはPythonプロセスのクラスターがハッシュ値を共有できるようにすることです。

整数は、範囲内の10進数である必要があります[0, 4294967295]。値0を指定すると、ハッシュのランダム化が無効になります。

例えば:

$ export PYTHONHASHSEED=0                            
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305

3
これはPython3.xにのみ当てはまりますが、Python 3は現在と未来であり、これがこれに対処する唯一の答えであるため、+ 1です。
Alexander Huszagh 2015年

8

ハッシュの結果は、32ビットプラットフォームと64ビットプラットフォームで異なります

計算されたハッシュが両方のプラットフォームで同じである場合は、使用を検討してください

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

6

推測では、AppEngineはPythonの64ビット実装を使用しており(-5768830964305142685は32ビットに適合しません)、Pythonの実装は32ビットです。異なる実装間で意味のある比較が可能なオブジェクトハッシュに依存することはできません。


6

これは、GoogleがPython2.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
このハッシュ関数が何のために使用されているのか、そしてその理由について何かコンテキストを共有できますか?
amcnabb 2012年

5

サインビットはどうですか?

例えば:

16進値0xADFE74A5は、符号なし2919134373と符号付きを表し-1375832923ます。現在の値は符号付きである必要があります(符号ビット= 1)が、Pythonはそれを符号なしとして変換し、64ビットから32ビットへの変換後に誤ったハッシュ値があります。

使用には注意してください:

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

3

文字列の多項式ハッシュ。1000000009239は任意の素数です。偶然に衝突する可能性は低いです。モジュラー演算はそれほど高速ではありませんが、衝突を防ぐために、これはの累乗をモジュロにするよりも信頼性があり2ます。もちろん、わざと衝突を見つけるのは簡単です。

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

2

PYTHONHASHSEEDの値は、ハッシュ値を初期化するために使用される場合があります。

試してみてください:

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

-3

おそらく、独自のアルゴリズムではなく、オペレーティングシステムが提供する機能を要求するだけです。

他のコメントが言うように、hashlibを使用するか、独自のハッシュ関数を記述してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.