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)で同じ結果が得られるハッシュ関数を使用するにはどうすればよいですか?
ドキュメントに記載されているように、組み込みhash()関数は、結果のハッシュを外部のどこかに格納するために設計されたnotです。オブジェクトのハッシュ値を提供したり、辞書に保存したりするために使用されます。また、実装固有です(GAEはPythonの修正バージョンを使用します)。チェックアウト:
>>> class Foo:
... pass
...
>>> a = Foo()
>>> b = Foo()
>>> hash(a), hash(b)
(-1210747828, -1210747892)
ご覧のとおり、hash()はオブジェクトの__hash__
SHAなどの「通常の」ハッシュアルゴリズムの代わりのメソッド。
上記を考えると、合理的な選択は hashlib モジュールを使用することです。
応答はまったく驚きではありません:実際には
_In [1]: -5768830964305142685L & 0xffffffff
Out[1]: 1934711907L
_
信頼できる応答を取得したい場合on ASCII文字列、下位32ビットをuint
として取得します。文字列のハッシュ関数は32です。ビットセーフでalmostポータブル。
反対に、___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の場合のように(循環なしの)オーバーフロー(オーバーフローなし)です。
ほとんどの回答は、これはプラットフォームが異なるためであることを示唆していますが、それ以外にもあります。 object.__hash__(self)
のドキュメント から:
デフォルトでは、
str
、bytes
、およびdatetime
オブジェクトの__hash__()
値は、予測不能なランダム値で「ソルト」されます。それらは個々のPythonプロセス内で一定のままですが、Pythonを繰り返し呼び出しても予測できません。これは、辞書挿入の最悪の場合のパフォーマンスであるO(n²)の複雑さを悪用する慎重に選択された入力によって引き起こされるサービス拒否に対する保護を提供することを目的としています。詳細については、 http://www.ocert.org/advisories/ocert-2011-003.html を参照してください。
ハッシュ値を変更すると、
dicts
、sets
およびその他のマッピングの反復順序に影響します。 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
に設定されている場合、ランダム値を使用して、str
、bytes
、およびdatetime
オブジェクトのハッシュをシードします。
PYTHONHASHSEED
が整数値に設定されている場合、ハッシュランダム化の対象となる型のhash()
を生成するための固定シードとして使用されます。その目的は、インタープリター自体のセルフテストなどの反復可能なハッシュを許可すること、またはpythonプロセスのハッシュ値を共有することを許可することです。
整数は_
[0, 4294967295]
_の範囲の10進数でなければなりません。値_0
_を指定すると、ハッシュのランダム化が無効になります。
例えば:
_$ export PYTHONHASHSEED=0
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
_
ハッシュの結果は32ビットプラットフォームと64ビットプラットフォームで異なります
計算されたハッシュが両方のプラットフォームで同じになる場合、使用を検討してください
def hash32(value):
return hash(value) & 0xffffffff
推測では、AppEngineはPython(-5768830964305142685は32ビットに収まらない)の64ビット実装を使用しており、Pythonは32異なる実装間で有意義に比較できるオブジェクトハッシュに依存することはできません。
これは、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
符号ビットはどうですか?
例えば:
16進値0xADFE74A5
は、符号なし2919134373
および符号付き-1375832923
を表します。正しい値は署名されている必要があります(符号ビット= 1)が、pythonはそれを符号なしとして変換し、64ビットから32ビットへの変換後に不正なハッシュ値があります。
使用には注意してください:
def hash32(value):
return hash(value) & 0xffffffff
文字列の多項式ハッシュ。 1000000009
および239
は任意の素数です。偶然衝突する可能性は低いです。モジュラー演算はそれほど高速ではありませんが、衝突を防ぐためには、2
のべき乗をモジュロとするよりも信頼性が高くなります。もちろん、意図的に衝突を見つけるのは簡単です。
mod=1000000009
def hash(s):
result=0
for c in s:
result = (result * 239 + ord(c)) % mod
return result % mod
[〜#〜] pythonhashseed [〜#〜] の値を使用して、ハッシュ値を初期化できます。
試してください:
PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))'