Jestem trochę zdezorientowany, co może, a czego nie może być używane jako klucz do dyktu w Pythonie.
dicked = {}
dicked[None] = 'foo' # None ok
dicked[(1,3)] = 'baz' # tuple ok
import sys
dicked[sys] = 'bar' # wow, even a module is ok !
dicked[(1,[3])] = 'qux' # oops, not allowed
Tak więc krotka jest niezmiennym typem, ale jeśli ukryję w niej listę, to nie może to być klucz… czy nie mógłbym równie łatwo ukryć listy w module?
Miałem niejasne pojęcie, że klucz musi być „haszowany”, ale zamierzam tylko przyznać, że nie znam szczegółów technicznych; Nie wiem, co się tu naprawdę dzieje. Co by się stało, gdybyś spróbował użyć list jako kluczy, z hashem jako, powiedzmy, lokalizacją w pamięci?
Odpowiedzi:
Na wiki Pythona znajduje się dobry artykuł na ten temat: Why Lists Can't Be Dictionary Keys . Jak tam wyjaśniono:
Można to zrobić bez łamania któregokolwiek z wymagań, ale prowadzi to do nieoczekiwanego zachowania. Listy są generalnie traktowane tak, jakby ich wartość pochodziła z wartości zawartości, na przykład podczas sprawdzania (nie) równości. Wielu spodziewałoby się - co zrozumiałe - że możesz użyć dowolnej listy
[1, 2]
aby uzyskać ten sam klucz, w którym musiałbyś trzymać się dokładnie tego samego obiektu listy. Ale wyszukiwanie według wartości jest przerywane, gdy tylko lista używana jako klucz zostanie zmodyfikowana, a wyszukiwanie według tożsamości wymaga, abyś trzymał dokładnie tę samą listę - co nie wymaga żadnej innej powszechnej operacji na liście (przynajmniej nie przychodzi mi do głowy ).Inne obiekty, takie jak moduły, i tak
object
robią znacznie większą sprawę z ich tożsamości obiektów (kiedy ostatnio miałeś wywołać dwa odrębne obiekty modułówsys
?) I mimo to są przez to porównywane. Dlatego mniej zaskakujące - a nawet oczekiwane - jest to, że używane jako klucze dyktowania również w tym przypadku porównują według tożsamości.źródło
Dlaczego nie mogę użyć listy jako klucza dyktowania w Pythonie?
(dla każdego, kto natknie się na to pytanie, szukając rozwiązania)
jak wyjaśnili inni tutaj, rzeczywiście nie możesz. Możesz jednak użyć jej reprezentacji łańcuchowej, jeśli naprawdę chcesz użyć swojej listy.
źródło
__eq__
. Ale jeśli przekonwertujesz je na łańcuchy, wszystko zostanie porównane według reprezentacji ciągu.Właśnie odkryłem, że możesz zmienić Listę na krotkę, a następnie użyć jej jako kluczy.
źródło
Problem polega na tym, że krotki są niezmienne, a listy nie. Rozważ następujące
Co powinno
d[li]
wrócić? Czy to ta sama lista? A co powieszd[[1,2,3]]
? Ma te same wartości, ale czy jest to inna lista?Ostatecznie nie ma satysfakcjonującej odpowiedzi. Na przykład, jeśli jedynym działającym kluczem jest klucz oryginalny, to jeśli nie masz odniesienia do tego klucza, nigdy więcej nie możesz uzyskać dostępu do wartości. Z każdym innym dozwolonym kluczem możesz skonstruować klucz bez odniesienia do oryginału.
Jeśli obie moje sugestie działają, masz bardzo różne klucze, które zwracają tę samą wartość, co jest więcej niż trochę zaskakujące. Jeśli działa tylko oryginalna zawartość, klucz szybko się zepsuje, ponieważ listy są modyfikowane.
źródło
d[li]
się, że pozostanie 5.d[[1,2,3]]
będzie odnosić się do innego obiektu listy jako klucza, więc będzie to KeyError. Naprawdę nie widzę jeszcze żadnego problemu ... poza tym, że zezwolenie na zbieranie kluczy może spowodować, że niektóre wartości dict będą niedostępne. Ale to jest problem praktyczny, a nie logiczny…d[list(li)]
bycie KeyError jest częścią problemu. W prawie każdym innym przypadku użycia ,li
byłyby nie do odróżnienia od nowej listy z identycznej treści. To działa, ale dla wielu jest sprzeczne z intuicją. Poza tym, kiedy ostatnio naprawdę musiałeś używać listy jako klucza dyktowania? Jedynym przypadkiem użycia, jaki mogę sobie wyobrazić, jest to, że i tak haszujesz wszystko według tożsamości, aw takim przypadku powinieneś to po prostu zrobić, zamiast polegać__hash__
i__eq__
być opartym na tożsamości.Oto odpowiedź http://wiki.python.org/moin/DictionaryKeys
Wyszukiwanie różnych list o tej samej zawartości dałoby różne wyniki, mimo że porównanie list o tej samej zawartości wskazywałoby, że są one równoważne.
A co z użyciem literału listy podczas wyszukiwania w słowniku?
źródło
Ponieważ listy są zmienne,
dict
klucze (iset
składowe) muszą być hashowalne, a haszowanie zmiennych obiektów jest złym pomysłem, ponieważ wartości skrótu powinny być obliczane na podstawie atrybutów instancji.W tej odpowiedzi podam kilka konkretnych przykładów, miejmy nadzieję, że dodam wartość do istniejących odpowiedzi. Każdy wgląd dotyczy elementów
set
infrastruktury danych.Przykład 1 : haszowanie zmiennego obiektu, gdzie wartość skrótu jest oparta na zmiennej charakterystyce obiektu.
Po mutacji
stupid
nie można go już znaleźć w dyktandzie, ponieważ zmienił się skrót. Tylko liniowe skanowanie listy kluczy dyktatu znajdujestupid
.Przykład 2 : ... ale dlaczego nie stałaby się wartością skrótu?
To również nie jest dobry pomysł, ponieważ równe obiekty powinny mieć identyczne hashowanie, aby można je było znaleźć w
dict
lubset
.Przykład 3 : ... ok, a co ze stałymi hashami we wszystkich instancjach ?!
Wydaje się, że wszystko działa zgodnie z oczekiwaniami, ale zastanów się, co się dzieje: kiedy wszystkie instancje twojej klasy generują tę samą wartość skrótu, wystąpi kolizja hashowania, ilekroć będzie więcej niż dwa wystąpienia kluczy w a
dict
lub w aset
.Znalezienie właściwej instancji z
my_dict[key]
lubkey in my_dict
(lubitem in my_set
) wymaga wykonania tylu sprawdzeń równości, ile jest instancjistupidlist3
w kluczach dykta (w najgorszym przypadku). W tym momencie cel słownika - wyszukiwanie O (1) - jest całkowicie pokonany. Jest to pokazane w następujących czasach (zrobionych za pomocą IPythona).Niektóre czasy na przykład 3
Jak widać, test członkostwa w naszym
stupidlists_set
jest nawet wolniejszy niż liniowe skanowanie całościlists_list
, podczas gdy masz oczekiwany super szybki czas wyszukiwania (współczynnik 500) w zestawie bez mnóstwa kolizji hash.TL; DR: możesz używać
tuple(yourlist)
jakodict
kluczy, ponieważ krotki są niezmienne i haszowalne.źródło
x
iz
jest taka sama. Jeśli coś w tym jest niejasne, otwórz nowe pytanie.hash(x)
ihash(z)
.Twój awnser można znaleźć tutaj:
Źródło i więcej informacji: http://wiki.python.org/moin/DictionaryKeys
źródło
Prosta odpowiedź na twoje pytanie jest taka, że lista klas nie implementuje skrótu metody, który jest wymagany dla każdego obiektu, który chce być używany jako klucz w słowniku. Jednak powodem, dla którego hash nie jest zaimplementowany w taki sam sposób, jak na przykład klasa krotki (na podstawie zawartości kontenera), jest to, że lista jest zmienna, więc edycja listy wymagałaby ponownego obliczenia skrótu, co może oznaczać listę w teraz znajduje się w niewłaściwym wiadrze w dolnej tabeli mieszania. Zauważ, że ponieważ nie możesz modyfikować krotki (niezmiennej), nie występuje ten problem.
Na marginesie, rzeczywista implementacja wyszukiwania dictobjects jest oparta na algorytmie D z Knuth Vol. 3, ust. 6.4. Jeśli masz tę książkę do dyspozycji, warto ją przeczytać, a jeśli jesteś naprawdę, naprawdę zainteresowany, możesz rzucić okiem na komentarze programistów dotyczące rzeczywistej implementacji dictobject tutaj. Opisuje szczegółowo, jak to działa. Jest też wykład w Pythonie na temat implementacji słowników, które mogą Cię zainteresować. W pierwszych minutach przechodzą przez definicję klucza i czym jest hash.
źródło
Zgodnie z dokumentacją Pythona 2.7.2:
Krotka jest niezmienna w tym sensie, że nie można dodawać, usuwać ani zastępować jej elementów, ale same elementy mogą być zmienne. Wartość skrótu listy zależy od wartości skrótu jej elementów, więc zmienia się, gdy zmieniasz elementy.
Użycie id dla skrótów list oznaczałoby, że wszystkie listy są porównywane inaczej, co byłoby zaskakujące i niewygodne.
źródło
hash = id
nie łamie niezmiennika na końcu pierwszego akapitu, pytanie brzmi, dlaczego nie jest to zrobione w ten sposób.coś takiego (kod pseudo):
Jeśli zastanawiasz się, jakie są dostępne opcje, których możesz użyć jako klucza do swojego słownika. Następnie
Możesz spróbować :
Jeśli działa dobrze, można go użyć jako klucza do słownika lub przekonwertować go na coś, co można skasować.
W skrócie :
tuple(<your list>)
.str(<your list>)
.źródło
dict
klucze muszą być hashowane. Listy są modyfikowalne i nie zapewniają prawidłowej metody mieszania .źródło