Windows XP, Python 2.5:
hash('http://stackoverflow.com') Result: 1934711907
Google App Engine ( http://shell.appspot.com/ ):
hash('http://stackoverflow.com') Result: -5768830964305142685
Dlaczego? Jak mogę skorzystać z funkcji skrótu, która da mi te same wyniki na różnych platformach (Windows, Linux, Mac)?
python
google-app-engine
hash
Denis T.
źródło
źródło
Odpowiedzi:
Użyj hashlib, który
hash()
został zaprojektowany do :i dlatego nie gwarantuje, że będzie taki sam we wszystkich implementacjach Pythona.
źródło
hashlib
nieco powolne do użytku niekryptograficznego?hash
95 ns,binascii.crc32
570 ns,hashlib.md5.digest()
1,42 us,murmur.string_hash
234 nshash
używa nowej, losowo generowanej wartości salt z każdą sesją Pythona. Więc będzie się zmieniać między sesjami Pythona.Jak stwierdzono w dokumentacji, wbudowana funkcja hash () nie jest przeznaczona do przechowywania wynikowych skrótów gdzieś na zewnątrz. Służy do dostarczania wartości skrótu obiektu, przechowywania ich w słownikach i tak dalej. Jest to również specyficzne dla implementacji (GAE używa zmodyfikowanej wersji Pythona). Sprawdzić:
>>> class Foo: ... pass ... >>> a = Foo() >>> b = Foo() >>> hash(a), hash(b) (-1210747828, -1210747892)
Jak widać, są one różne, ponieważ hash () używa
__hash__
metody obiektu zamiast „normalnych” algorytmów haszujących, takich jak SHA.Biorąc pod uwagę powyższe, racjonalnym wyborem jest użycie modułu hashlib .
źródło
int(hashlib.md5(repr(self)).hexdigest(), 16)
(zakładając, żeself.__repr__
zostały zdefiniowane jako identyczne obiekty iff są identyczne). Jeśli 32 bajty są zbyt długie, możesz oczywiście zmniejszyć rozmiar, przecinając ciąg szesnastkowy przed konwersją.__repr__
jest wystarczająco unikalny, możesz po prostu użyćstr.__hash__
(tj.hash(repr(self))
), Ponieważ dykty nie mieszają nierównych obiektów z tym samym hashem. Działa to tylko wtedy, gdy obiekt jest na tyle trywialny, że repr może oczywiście reprezentować tożsamość.a
ib
jak mogę użyć modułu hashlib, aby zobaczyć, że obiekty są identyczne?__hash__()
i__eq__()
metody w swojej klasie .Odpowiedź nie jest żadnym zaskoczeniem: w rzeczywistości
In [1]: -5768830964305142685L & 0xffffffff Out[1]: 1934711907L
więc jeśli chcesz uzyskać niezawodne odpowiedzi na łańcuchach ASCII , po prostu pobierz niższe 32 bity jako
uint
. Funkcja skrótu dla ciągów znaków jest 32-bitowa i prawie przenośna.Z drugiej strony nie możesz w ogóle polegać na uzyskaniu
hash()
dowolnego obiektu, dla którego nie zdefiniowałeś jawnie__hash__
metody jako niezmiennej.W przypadku ciągów ASCII działa tylko dlatego, że hash jest obliczany na podstawie pojedynczych znaków tworzących ciąg, jak poniżej:
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
gdzie
c_mul
funkcją jest mnożenie „cykliczne” (bez przepełnienia) jak w C.źródło
Większość odpowiedzi sugeruje, że dzieje się tak z powodu różnych platform, ale to nie wszystko. Z dokumentacji
object.__hash__(self)
:Nawet uruchomienie na tej samej maszynie da różne wyniki w różnych wywołaniach:
$ python -c "print(hash('http://stackoverflow.com'))" -3455286212422042986 $ python -c "print(hash('http://stackoverflow.com'))" -6940441840934557333
Podczas:
$ python -c "print(hash((1,2,3)))" 2528502973977326415 $ python -c "print(hash((1,2,3)))" 2528502973977326415
Zobacz także zmienną środowiskową
PYTHONHASHSEED
:Na przykład:
$ export PYTHONHASHSEED=0 $ python -c "print(hash('http://stackoverflow.com'))" -5843046192888932305 $ python -c "print(hash('http://stackoverflow.com'))" -5843046192888932305
źródło
Wyniki haszowania są różne dla platform 32- i 64-bitowych
Jeśli obliczony hash będzie taki sam na obu platformach, rozważ użycie
def hash32(value): return hash(value) & 0xffffffff
źródło
Domyślam się, że AppEngine używa 64-bitowej implementacji Pythona (-5768830964305142685 nie zmieści się w 32 bitach), a Twoja implementacja Pythona jest 32-bitowa. Nie można polegać na tym, że skróty obiektów są w znaczący sposób porównywalne między różnymi implementacjami.
źródło
Oto funkcja skrótu, której Google używa w środowisku produkcyjnym dla Pythona 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
źródło
A co z kawałkiem znaku?
Na przykład:
Wartość szesnastkowa
0xADFE74A5
reprezentuje bez znaku2919134373
i ze znakiem-1375832923
. Bieżąca wartość musi być podpisana (bit znaku = 1), ale Python konwertuje ją jako niepodpisaną i mamy niepoprawną wartość skrótu po translacji z 64 do 32 bitów.Uważaj, używając:
def hash32(value): return hash(value) & 0xffffffff
źródło
Hasz wielomianowy dla ciągów.
1000000009
i239
są dowolnymi liczbami pierwszymi. Mało prawdopodobne, aby zderzyły się przez przypadek. Arytmetyka modularna nie jest bardzo szybka, ale w zapobieganiu kolizjom jest to bardziej niezawodne niż przyjmowanie jej modulo na potęgę2
. Oczywiście łatwo jest celowo znaleźć kolizję.mod=1000000009 def hash(s): result=0 for c in s: result = (result * 239 + ord(c)) % mod return result % mod
źródło
Wartość PYTHONHASHSEED może zostać użyta do zainicjowania wartości skrótu.
Próbować:
PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))'
źródło
Prawdopodobnie po prostu pyta o funkcję dostarczoną przez system operacyjny, a nie o własny algorytm.
Jak mówią inne komentarze, użyj hashlib lub napisz własną funkcję skrótu.
źródło