2011-10-04 2 views
22

Po pytaniu od this, chciałbym wiedzieć, kiedy jest obiekt hash obiektu Pythona obliczony?Kiedy jest obliczany hash obiektu Pythona i dlaczego skrót wartości -1 jest inny?

  1. Przy instancji __init__ czasie
  2. Przy pierwszym __hash__() nazywa,
  3. każdym razem __hash__() nazywa, lub
  4. Wszelkie inne okazji I może brakować?

Czy to może się różnić w zależności od typu obiektu?

Dlaczego inne liczby całkowite są równe ich hash?

+0

3 jest obecnie jak mam [czytaj] (http://www.laurentluce.com/posts/ python-dictionary-implementation /) jest buforowany przy pierwszym wywołaniu.Zakładam, że druga opcja jest właściwa, ale ponieważ nie jestem pewien, nie będę go publikować jako odpowiedzi :) – rplnt

+0

@rplnt: wrong; to tylko po rozmowie ze słownikiem. Jego skrót zostanie zapisany w słowniku, ale nie dotyczy to ogólnego skrótu. –

+0

@ChrisMorgan Właściwie nie uważam, że Python 'dict' buforuje wartości mieszania dla swoich kluczy. Oczywiście poszczególne klasy mogą robić, co im się podoba w funkcji '__hash__', więc artykuł, o którym mowa powyżej, powiedział, że' str' buforuje swoje wartości mieszania. – max

Odpowiedz

19

Hash jest zwykle obliczany za każdym razem, gdy jest używany, ponieważ można go łatwo sprawdzić (patrz poniżej). Oczywiście dowolny konkretny obiekt może za darmo buforować jego skrót. Na przykład łańcuchy CPython robią to, ale krotki nie (patrz np. this rejected bug report z powodów).

Wartość mieszania -1signalizes an error na CPython. Wynika to z faktu, że C nie ma wyjątków, więc musi użyć wartości zwracanej. Gdy obiekt Pythona w wersji __hash__ zwróci wartość -1, program CPython zmieni ją w trybie cichym na -2.

Zobacz sam:

class HashTest(object): 
    def __hash__(self): 
     print('Yes! __hash__ was called!') 
     return -1 

hash_test = HashTest() 

# All of these will print out 'Yes! __hash__ was called!': 

print('__hash__ call #1') 
hash_test.__hash__() 

print('__hash__ call #2') 
hash_test.__hash__() 

print('hash call #1') 
hash(hash_test) 

print('hash call #2') 
hash(hash_test) 

print('Dict creation') 
dct = {hash_test: 0} 

print('Dict get') 
dct[hash_test] 

print('Dict set') 
dct[hash_test] = 0 

print('__hash__ return value:') 
print(hash_test.__hash__()) # prints -1 
print('Actual hash value:') 
print(hash(hash_test)) # prints -2 
5

Od here:

wartość hash -1 jest zarezerwowany (nie jest to wykorzystywane do błędów flag w wykonaniu C). Jeśli algorytm mieszający generuje tę wartość, używamy po prostu -2.

Jako wartość całkowita jest wartością całkowitą, która jest po prostu zmieniana od razu.

1

Łatwo zobaczyć, że opcja nr 3 odnosi się do zdefiniowanych przez użytkownika obiektów. Pozwala to zmienić mieszanie, jeśli zmienisz obiekt, ale jeśli kiedykolwiek użyjesz tego obiektu jako klucza słownika, musisz się upewnić, że hasz się nie zmienia.

>>> class C: 
    def __hash__(self): 
     print("__hash__ called") 
     return id(self) 


>>> inst = C() 
>>> hash(inst) 
__hash__ called 
43795408 
>>> hash(inst) 
__hash__ called 
43795408 
>>> d = { inst: 42 } 
__hash__ called 
>>> d[inst] 
__hash__ called 

Opcja użycia ciągów # 2: obliczają wartość skrótu raz i buforują wynik. Jest to bezpieczne, ponieważ łańcuchy są niezmienne, więc skrót nie może się nigdy zmienić, ale jeśli podklasa str wynik może nie być niezmienny, więc metoda __hash__ będzie wywoływana za każdym razem. Krotki są zwykle uważane za niezmienne, więc możesz pomyśleć, że hash może być zbuforowany, ale w rzeczywistości krotka krotka zależy od skrótu jej zawartości i może zawierać zmienne wartości.

Dla @max którzy nie wierzą, że podklasy str można zmodyfikować skrót:

>>> class C(str): 
    def __init__(self, s): 
     self._n = 1 
    def __hash__(self): 
     return str.__hash__(self) + self._n 


>>> x = C('hello') 
>>> hash(x) 
-717693723 
>>> x._n = 2 
>>> hash(x) 
-717693722 
+0

Po przekazaniu krotki zawierającej zmienne wartości jako argumentu do wbudowanego obiektu w funkcji skrótu zostanie zgłoszony wyjątek TypeError. Nie jest to spowodowane tym, że krotki nie buforują wartości mieszania. Link na początku [@ PetrViktorin's answer above] (http://stackoverflow.com/a/7648538/336527) dostarcza wyjaśnienia. Zobacz także [komentarze Guido] (http://mail.python.org/pipermail/python-dev/2003-August/037424.html). Ponadto, czy na pewno hash nie jest buforowane dla podklas str? Wydaje się, że zwraca tę samą wartość, co str.hash, który jest automatycznie buforowany. – max

+0

@max, dodałem przykład pokazujący, że mieszanie podklasy 'str' nie jest buforowane. – Duncan

+0

ah tak, tak .. Myślę, że myślałem, że jeśli nie zdefiniujesz '__hash__', jest to buforowane, ale wtedy jest to oczywiste, ponieważ w tym przypadku po prostu używa' str .__ hash__'. – max