2009-04-27 15 views
80

Windows XP, Python 2.5:Zbudowany w python hash function()

hash('http://stackoverflow.com') Result: 1934711907 

Google App Engine (http://shell.appspot.com/):

hash('http://stackoverflow.com') Result: -5768830964305142685 

Dlaczego tak jest? Jak mogę mieć funkcję skrótu, która da mi takie same wyniki na różnych platformach (Windows, Linux, Mac)?

+14

to zawdzięczam fakt Twój WinXP 32bit jest platforma Google jest natomiast 64-bitowy –

Odpowiedz

54

Zastosowanie hashlib jak hash()was designed to be used to:

szybko porównać klucze słownika podczas słowniku odnośnika

i dlatego nie gwarantuje, że będzie taka sama we wdrożeniach Pythona.

+5

nie są funkcje hash w 'hashlib' nieco powolne dla Niekryptograficzne posługiwać się? –

+0

@Bandon: są oni? testy porównawcze? łatki? – SilentGhost

+8

Są one rzeczywiście bardzo powolne w porównaniu do ogólnych funkcji skrótu, takich jak Jenkins, Bernstein, FNV, MurmurHash i wiele innych. Jeśli chcesz utworzyć własną strukturę przypominającą tablicę asocjacyjną, sugeruję przejrzenie uthash.h http://uthash.sourceforge.net/ – lericson

-3

Prawdopodobnie prosi tylko o podanie funkcji systemu operacyjnego, zamiast własnego algorytmu.

Jak mówią inne komentarze, użyj hashlib lub wpisz własną funkcję skrótu.

88

Jak podano w dokumentacji, wbudowany hash() funkcja jest nie przeznaczone do magazynowania wynikające mieszań gdzieś na zewnątrz. Służy do zapewnienia wartości mieszania obiektu, do przechowywania ich w słownikach i tak dalej. Jest to również specyficzne dla implementacji (GAE używa zmodyfikowanej wersji Pythona). Sprawdź:

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

Jak widać, są one różne, jak hash() używa obiektu __hash__ metodę zamiast „normalnych” mieszania algorytmy, takie jak SHA.

Biorąc pod uwagę powyższe, racjonalnym wyborem jest użycie modułu hashlib.

+0

Dziękujemy! Przybyłem tu zastanawiając się, dlaczego zawsze otrzymam różne wartości mieszania dla identycznych obiektów powodujące nieoczekiwane zachowanie w przypadku dyktów (które indeksują według typu mieszania, a nie sprawdzają równość). Szybkim sposobem generowania własnego skrótu int z hashlib.md5 jest 'int (hashlib.md5 (repr (self)). Hexdigest(), 16)' (zakładając, że 'self .__ repr__' został zdefiniowany jako identyczne obiekty iff są identyczne). Jeśli 32 bajty są zbyt długie, możesz zmniejszyć rozmiar, przecinając łańcuch szesnastkowy przed konwersją. –

+1

Po drugie, jeśli '__repr__' jest wystarczająco unikalny, możesz po prostu użyć' str .__ hash__' (tj. 'Hash (repr (self))'), ponieważ dyktemy nie mieszają nie-równych obiektów z tym samym hash. Działa to tylko wtedy, gdy obiekt jest na tyle banalny, że repr może oczywiście reprezentować tożsamość. –

+0

Tak więc, w twoim przykładzie z dwoma obiektami 'a' i' b', jak mogę użyć modułu hashlib, aby zobaczyć, że obiekty są identyczne? – Garrett

6

Na domysły AppEngine używa 64-bitowej implementacji Pythona (-5768830964305142685 nie mieści się w 32 bitach), a implementacja Pythona ma 32 bity. Nie można polegać na tym, że skróty obiektów są sensownie porównywalne między różnymi implementacjami.

32

Odpowiedź jest absolutnie nie dziwi: w rzeczywistości

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

więc jeśli chcesz uzyskać wiarygodne odpowiedzi na ciągi ASCII, po prostu dolne 32 bity jak uint. Funkcja mieszania dla łańcuchów jest 32-bitowym bezpiecznym i prawie przenośna.

Po drugiej stronie nie można w ogóle polegać na uzyskaniu hash() dowolnego obiektu, dla którego nie zdefiniowano jawnie metody __hash__ jako niezmiennej.

ciągu ciągów ASCII to działa tylko dlatego, że hash oblicza się na pojedynczych znaków tworzących łańcuch, jak następuje:

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 funkcja c_mul jest „cykliczny” mnożenia (bez przelewu), jak w DO.

8

wyniki Hash waha się od platform 32-bitowych i 64-bitowych

Jeżeli obliczony mieszania powinna być taka sama na obu platformach rozważyć użycie

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

Co bitu znaku?

Na przykład:

wartość Hex 0xADFE74A5 reprezentuje niepodpisany 2919134373 i podpisany -1375832923. Poprawna wartość musi być podpisana (znak bit = 1), ale python konwertuje ją jako niepodpisaną, a my mamy niepoprawną wartość skrótu po przetłumaczeniu z 64 na 32-bitowe.

Bądź ostrożny przy użyciu:

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

Jest to funkcja hash, że Google wykorzystuje w produkcji 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 
+7

Czy możesz udostępnić dowolny kontekst dotyczący tego, do czego służy ta funkcja skrótu i ​​dlaczego? – amcnabb

3

Wielomian hash dla ciągów. 1000000009 i 239 są arbitralne liczby pierwsze. Jest mało prawdopodobne, aby kolizja przypadkowa. Modułowa arytmetyka nie jest bardzo szybka, ale w celu zapobiegania kolizjom jest to bardziej niezawodne niż użycie modulo o mocy 2. Oczywiście łatwo jest znaleźć kolizję celowo.

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

Wartość PYTHONHASHSEED może być użyta do zainicjowania wartości skrótów.

Spróbuj:

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

Większość odpowiedzi sugerują, że to właśnie z powodu różnych platformach, ale nie więcej. Od the documentation of object.__hash__(self):

Domyślnie wartości __hash__() z str, bytes i datetime obiektów są „solone” z nieprzewidywalnym wartości losowej. Mimo że pozostają one stałe w ramach pojedynczego procesu Pythona, , nie można ich przewidzieć między powtórzonymi wywołaniami Pythona.

ten przeznaczony jest do ochrony przed zaprzeczenie of Service spowodowane starannie wybranych wejść wykorzystujących najgorszym przypadku wydajność wstawiania dict O (n²) komplikacji. Aby uzyskać szczegółowe informacje, patrz http://www.ocert.org/advisories/ocert-2011-003.html.

Zmiana wartości mieszania wpływa na kolejność iteracji dicts, sets i inne odwzorowania. Python nigdy nie udzielił gwarancji na to zamówienie (zazwyczaj jest to wersja 32-bitowa i 64-bitowa).

Nawet działa na tej samej maszynie dadzą różne wyniki całej inwokacji:

$ 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 również zmienna środowiskowa PYTHONHASHSEED:

Jeśli ta zmienna nie jest ustawiona lub ustawiona na random, używana jest wartość losowa do wysiewania haszy obiektów str, i datetime.

PYTHONHASHSEED Jeśli ustawiony jest liczbą całkowitą, a stosuje się go w postaci stałej nasiona do generowania hash() typów objętych mieszania randomizacją.

Jego celem jest umożliwienie powtarzalnego haszowania, na przykład dla autotranslatorów dla samego tłumacza, lub zezwolenie na klastry procesów Pythona na udostępnianie wartości skrótów.

Liczba całkowita musi być liczbą dziesiętną z zakresu [0, 4294967295]. Określenie wartości 0 spowoduje wyłączenie losowania mieszania.

Na przykład:

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

Dotyczy to tylko języka Python 3.x, ale ponieważ Python 3 jest teraźniejszością i przyszłością i jest to jedyna odpowiedź, która to rozwiązuje, +1. –