2011-02-09 10 views
9

Próbuję użyć obiektu jako klucza w słowniku Pythona, ale zachowuję się w sposób, którego nie mogę całkiem zrozumieć.obiekty jako klucze w słownikach Pythona

Najpierw utworzyć słownik z mojego obiektu jako klucz:

package_disseminators = { 
    ContentType("application", "zip", "http://other/property") : "one", 
    ContentType("application", "zip") : "two" 
} 

Teraz utworzyć inny obiekt, który jest „taki sam”, jak ten, który jest kluczowy.

content_type = ContentType("application", "zip", "http://other/property") 

daję przedmiotu ContentType niestandardowy __eq__ i niestandardowych __str__ metody, takie, że sposób __eq__ porównuje wartości __str__.

Teraz, niektóre interaktywne Python:

>>> for key in package_disseminators: 
...  if key == content_type: 
...    print "match" 
...  else: 
...    print "no match" 
... 
no match 
match 

>>> content_type in package_disseminators.keys() 
True 

Ok, więc wygląda na to mojego obiektu jest zdecydowanie są zidentyfikowane odpowiednio jako klucz, tak:

>>> package_disseminators[content_type] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: (& (type="application/zip") (packaging="http://other/property")) 

Er ... ok? Więc content_type znajduje się na liście packages_disseminators.keys(), ale nie jest kluczem?

>>> package_disseminators.has_key(content_type) 
False 

Najwyraźniej nie.

Zakładam, że proces porównywania, który Python używa do określenia równości, różni się od prostej instrukcji "in" na liście i faktycznie szuka klucza w dykcie, ale nie wiem jak. Wszelkie wskazówki lub spostrzeżenia?

Odpowiedz

17

Z dokumentacji Pythona:

klucze słownika są prawie arbitralne wartości. Wartości, które nie są zgodne z hashable, to znaczy wartości zawierające listy , słowniki lub inne zmienne typy (które są porównywane przez wartość zamiast przez tożsamość obiektu) mogą nie być używane jako klucze.

Hashable jest zdefiniowany następująco

Obiekt jest hashable jeśli ma skrótu wartość, która nigdy się nie zmienia podczas życia (potrzebuje __hash__() metody) i może być porównywany do innych obiektów (wymaga to metody __eq__() lub __cmp__()). Obiekty z możliwością dopasowania, które mają równe wartości, muszą mieć tę samą wartość skrótu: .

Hashability czyni przedmiot użyteczny jako kluczowego słownik i członkiem zestaw, ponieważ te struktury danych używać wartości hash wewnętrznie.

Więc jeśli chcesz to zrobić, trzeba zastąpić metodę domyślną __hash__() na swoim obiekcie (patrz komentarz poniżej Steven Rumbalski dla dalszych wyjaśnień).


>>> content_type in package_disseminators.keys() 
True 

przypuszczam to działa bo dict.keys() zwraca listę, a __contains__ prawdopodobnie sprawdza równości, ale nie dla tych samych skrótów.

+6

Kilka dodatkowych wyjaśnień: Twój obiekt ma już metodę '__hash__' dziedziczoną po' obiekcie'. Ale domyślna implementacja zwraca unikalną wartość dla każdej instancji, więc dwa równe wystąpienia będą miały różne wartości haszowania, chyba że zapewnimy lepszą implementację. 'has_key' porównuje wartości mieszania,' in' sprawdza równość, dlatego 'has_key' kończy się niepowodzeniem, a' in' kończy się powodzeniem w twoich przykładach. –

+0

Witajcie. Świetnie, dzięki za to, bardzo docenione! –

11

Ponieważ dicts to tabele mieszania pod maską, musisz zdefiniować zarówno __eq__, jak i __hash__, aby działały.

Podstawową zasadą jest:

  • Dla obiektów, które __eq__ porównuje równe, __hash__ musi wrócić tego samego skrótu.

Z Twojego opisu, coś

def __hash__(self): 
    return hash(str(self)) 

powinno działać.

+0

Dzięki za wdrożenie '__hash__'! Niestety nie mogę przydzielić dwóch poprawnych odpowiedzi na pytanie, chociaż kombinacja twoich i innych to wszystko, czego potrzebuję. Pozdrawiam, R. –

Powiązane problemy