Co robi hash w Pythonie?

Widziałem przykład kodu, który gdzie hash funkcja jest stosowana do krotki. W rezultacie zwraca ujemną liczbę całkowitą. Ciekawe co robi ta funkcja. Google nie pomaga. Znalazłem stronę, która wyjaśnia, jak jest obliczany hash, ale nie wyjaśnia, dlaczego potrzebujemy tej funkcji.

 44
Author: Roman, 2013-07-11

4 answers

Hash jest liczbą całkowitą o stałym rozmiarze, która identyfikuje określoną wartość . Każda wartość musi mieć swój własny hash, więc dla tej samej wartości otrzymasz ten sam hash, nawet jeśli nie jest to ten sam obiekt.

>>> hash("Look at me!")
4343814758193556824
>>> f = "Look at me!"
>>> hash(f)
4343814758193556824

Wartości skrótu muszą być tworzone w taki sposób, aby otrzymane wartości były równomiernie rozłożone, aby zmniejszyć liczbę kolizji skrótu. Kolizje Hash są wtedy, gdy dwie różne wartości mają ten sam hash. Dlatego też stosunkowo niewielkie zmiany często skutkują bardzo różne hasze.

>>> hash("Look at me!!")
6941904779894686356

Te liczby są bardzo przydatne, ponieważ umożliwiają szybkie wyszukanie wartości w dużym zbiorze wartości. Dwa przykłady ich użycia to set i dict. W list, Jeśli chcesz sprawdzić, czy wartość jest na liście, z if x in values:, Python musi przejść przez całą listę i porównać x z każdą wartością na liście values. Może to potrwać długo list. W set, Python śledzi każdy hash, a kiedy wpiszesz if x in values:, Python otrzyma hash-wartość dla x, poszukaj go w wewnętrznej strukturze, a następnie porównaj x z wartościami, które mają ten sam hash co x.

Ta sama metodologia jest używana do wyszukiwania słownika. To sprawia, że wyszukiwanie w set i dict jest bardzo szybkie, podczas gdy wyszukiwanie w {[5] } jest powolne. Oznacza to również, że możesz mieć obiekty nie hashowalne w list, ale nie w set lub jako klucze w dict. Typowym przykładem obiektów niehaszowalnych jest dowolny obiekt, który jest zmienny, co oznacza, że można zmienić jego wartość. Jeśli masz zmienny obiekt, nie powinien on być hashowalny, ponieważ jego hash zmieni się w czasie jego życia, co spowodowałoby wiele zamieszania, ponieważ obiekt może skończyć się pod niewłaściwą wartością hash w słowniku.

Zauważ, że hash wartości musi być taki sam tylko dla jednego uruchomienia Pythona. W Pythonie 3.3 będą się zmieniać dla każdego nowego uruchomienia Pythona:

$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
1849024199686380661
>>> 
$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
-7416743951976404299

Jest to, aby trudniej odgadnąć, jaką wartość hash będzie miał dany ciąg znaków, co jest ważnym funkcja bezpieczeństwa dla aplikacji internetowych itp.

Wartości skrótu nie powinny być zatem przechowywane na stałe. Jeśli chcesz używać wartości skrótu w sposób stały, możesz przyjrzeć się bardziej "poważnym" rodzajom skrótów, kryptograficzne funkcje skrótu, które mogą być używane do tworzenia możliwych do zweryfikowania sum kontrolnych plików itp.

 86
Author: Lennart Regebro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 17:43:21

TL;DR:

Proszę zapoznać się ze słowniczkiem : hash() jest używany jako skrót do porównywania obiektów, obiekt jest uważany za hashable, jeśli można go porównać z innymi obiektami. dlatego używamy hash(). Jest również używany do dostępu do elementów dict i set, które są zaimplementowane jako tabele hash o rozmiarze w CPython.

Względy techniczne

  • zazwyczaj porównywanie obiektów (które mogą obejmować kilka poziomów rekurencji) jest drogie.
  • najlepiej, aby funkcja hash() była o rząd wielkości (lub kilka) tańsza.
  • porównywanie dwóch skrótów jest łatwiejsze niż porównywanie dwóch obiektów, tutaj znajduje się Skrót.

Jeśli czytasz o jak słowniki są implementowane , używają tabel skrótów, co oznacza, że wyprowadzenie klucza z obiektu jest kamieniem węgielnym do pobierania obiektów w słownikach w O(1). To jednak bardzo zależy od funkcji hash, aby być odporne na kolizje . najgorszy przypadek uzyskania elementu w słowniku jest w rzeczywistości O(n).

Z tego względu, mutowalne obiekty zazwyczaj nie są hashowalne. Właściwość hashable oznacza, że możesz użyć obiektu jako klucza. Jeśli wartość skrótu zostanie użyta jako klucz i zawartość tego samego obiektu ulegnie zmianie, to co powinna zwrócić funkcja skrótu? To ten sam klucz czy inny? To zależy od tego, jak zdefiniujesz swoją funkcję haszującą.

Nauka przez przykład:

Wyobraź sobie, że mamy tę klasę:

>>> class Person(object):
...     def __init__(self, name, ssn, address):
...         self.name = name
...         self.ssn = ssn
...         self.address = address
...     def __hash__(self):
...         return hash(self.ssn)
...     def __eq__(self, other):
...         return self.ssn == other.ssn
... 

Uwaga: wszystko to opiera się na założeniu, że SSN nigdy nie zmienia się dla jednostki (nawet nie wiem, gdzie właściwie zweryfikować ten fakt z autorytatywnego źródła).

I mamy Boba:

>>> bob = Person('bob', '1111-222-333', None)

Bob idzie do sędziego, aby zmienić imię:

>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')

Oto co wiemy:

>>> bob == jim
True

Ale są to dwa różne obiekty z inną przydzieloną pamięcią, Tak jak dwa różne rekordy tej samej osoby:

>>> bob is jim
False

Teraz pojawia się część, w której Hash() jest przydatny:

>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'

Zgadnij co:

>>> dmv_appointments[jim] #?
'tomorrow'

Z dwóch różnych rekordów możesz uzyskać dostęp do tych samych informacji. Teraz spróbuj tego:

>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True
Co się stało? To kolizja. Ponieważ hash(jim) == hash(hash(jim)) które są obiema liczbami całkowitymi btw, musimy porównać dane wejściowe __getitem__ ze wszystkimi elementami, które się zderzają. Wbudowany int nie ma atrybutu ssn, więc potknie się.
>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>

W tym ostatnim przykład, pokazuję, że nawet przy kolizji, porównanie jest wykonywane, obiekty nie są już równe, co oznacza, że z powodzeniem podnosi KeyError.

 18
Author: dnozay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-11 16:46:42

Python docs for hash() państwo:

Wartości Hash są liczbami całkowitymi. Są one używane do szybkiego porównywania kluczy słownikowych podczas wyszukiwania słownika.

Słowniki Pythona są zaimplementowane jako tabele skrótów. Więc za każdym razem, gdy używasz słownika, hash() jest wywoływany na kluczach, które przekazujesz do przypisania lub wyszukiwania.

Dodatkowo, docs for the dict type state:

Wartości, które nie są , czyli wartości klucze zawierające listy, słowniki lub inne zmienne typy (które są porównywane według wartości, a nie według tożsamości obiektu) nie mogą być używane jako klucze.

 2
Author: Jonathon Reinhart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-11 05:39:13

Hash jest używany przez słowniki i zestawy do szybkiego wyszukania obiektu. Dobrym punktem wyjścia jest artykuł Wikipedii na tabele hash.

 1
Author: NPE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-11 05:39:53