Obiekt typu własnego jako klucz słownikowy
Co muszę zrobić, aby używać obiektów niestandardowego typu jako kluczy w słowniku Pythona ( gdzie nie chcę, aby "object id" działał jako klucz), np.
class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length
Chciałbym użyć MyThing ' s jako kluczy, które są uważane za te same, jeśli nazwa i lokalizacja są takie same. Od C # / Java jestem przyzwyczajony do nadpisywania i dostarczania metody equals i hashcode, i obiecuję nie mutować niczego, od czego zależy hashcode.
Co muszę zrobić w Pythonie, aby to osiągnąć ? Powinienem w ogóle ?
(w prostym przypadku, jak tutaj, może lepiej byłoby umieścić krotkę (nazwę, lokalizację) jako klucz - ale pomyśl, że chciałbym, aby klucz był obiektem)
3 answers
Należy dodać 2 metody , notatkę __hash__
i __eq__
:
class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length
def __hash__(self):
return hash((self.name, self.location))
def __eq__(self, other):
return (self.name, self.location) == (other.name, other.location)
def __ne__(self, other):
# Not strictly necessary, but to avoid having both x==y and x!=y
# True at the same time
return not(self == other)
Dokumentacja Pythona dict definiuje te wymagania na obiektach kluczowych, tzn. muszą być hashable .
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
2017-04-18 04:13:18
Alternatywą w Pythonie 2.6 lub nowszym jest użycie collections.namedtuple()
-- zapisuje pisanie żadnych specjalnych metod:
from collections import namedtuple
MyThingBase = namedtuple("MyThingBase", ["name", "location"])
class MyThing(MyThingBase):
def __new__(cls, name, location, length):
obj = MyThingBase.__new__(cls, name, location)
obj.length = length
return obj
a = MyThing("a", "here", 10)
b = MyThing("a", "here", 20)
c = MyThing("c", "there", 10)
a == b
# True
hash(a) == hash(b)
# True
a == c
# False
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
2011-02-04 20:58:30
Nadpisujesz __hash__
, jeśli chcesz użyć specjalnej semantyki hashowej i __cmp__
lub __eq__
, aby Twoja klasa była użyteczna jako klucz. Obiekty, które porównują równe muszą mieć tę samą wartość skrótu.
Python oczekuje, że __hash__
zwróci liczbę całkowitą, zwracanie Banana()
nie jest zalecane:)
Klasy zdefiniowane przez Użytkownika mają __hash__
domyślnie wywołujące id(self)
, jak zauważyłeś.
Jest kilka dodatkowych wskazówek z dokumentacji.:
Klasy dziedziczące a
__hash__()
metoda z klasy rodzica, ale zmiana znaczenie__cmp__()
lub__eq__()
taki, że zwrócona wartość hash jest już nie odpowiednie (np. przez przejście na koncepcję opartą na wartościach równość zamiast domyślnej równość tożsamościowa) może wyraźnie zaznaczają się jako unhashable by setting__hash__ = None
w definicji klasy. W ten sposób oznacza, że nie tylko Klasa podnosi odpowiednie TypeError gdy program próbuje odzyskać ich wartość hash, ale oni zostanie również poprawnie zidentyfikowany jako nieszkodliwe podczas sprawdzaniaisinstance(obj, collections.Hashable)
(w przeciwieństwie do klas, które definiują własne__hash__()
jawnie podnieść TypeError).
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
2011-02-04 19:44:46