Do celów buforowania potrzebuję wygenerować klucz pamięci podręcznej z argumentów GET, które są obecne w dict.
Obecnie używam sha1(repr(sorted(my_dict.items())))
( sha1()
jest to wygodna metoda wykorzystująca wewnętrznie hashlib ), ale jestem ciekawy, czy istnieje lepszy sposób.
python
hash
dictionary
ThiefMaster
źródło
źródło
{'a': 1, 'b':2}
jest semantycznie taki sam jak{'b':2, 'a':1}
). Nie użyłem go jeszcze do niczego zbyt skomplikowanego, więc YMMV, ale mile widziane opinie.Odpowiedzi:
Jeśli twój słownik nie jest zagnieżdżony, możesz utworzyć zestaw zamrażający z elementami dyktu i użyć
hash()
:Jest to znacznie mniej intensywne obliczeniowo niż generowanie ciągu JSON lub reprezentacja słownika.
AKTUALIZACJA: Zapoznaj się z komentarzami poniżej, dlaczego takie podejście może nie dać stabilnych wyników.
źródło
hash()
funkcja nie zapewnia stabilnych wyników. Oznacza to, że przy tych samych danych wejściowych zwraca różne wyniki z różnymi instancjami tego samego interpretera języka Python. Dla mnie wygląda na to, że za każdym razem, gdy uruchamiany jest interpreter, generowana jest jakaś wartość ziarna.Użycie
sorted(d.items())
nie wystarczy, aby uzyskać stabilny repr. Niektóre wartościd
mogą być również słownikami, a ich klucze nadal będą pojawiać się w dowolnej kolejności. Jeśli wszystkie klawisze są strunami, wolę używać:To powiedziawszy, jeśli skróty muszą być stabilne na różnych komputerach lub wersjach Pythona, nie jestem pewien, czy jest to kuloodporne. Możesz chcieć dodać argumenty
separators
i,ensure_ascii
aby uchronić się przed wszelkimi zmianami wartości domyślnych. Byłbym wdzięczny za komentarze.źródło
ensure_ascii
Argumentem by chronić przed tym całkowicie hipotetycznego problemu.make_hash
. gist.github.com/charlax/b8731de51d2ea86c6eb9default=str
dodumps
polecenia. Wygląda na to, że dobrze działa.EDYCJA : Jeśli wszystkie twoje klucze są ciągami , to przed kontynuowaniem czytania tej odpowiedzi zapoznaj się ze znacznie prostszym (i szybszym) rozwiązaniem Jacka O'Connora (które działa również w przypadku haszowania zagnieżdżonych słowników).
Chociaż odpowiedź została zaakceptowana, tytuł pytania brzmi „Haszowanie słownika Pythona”, a odpowiedź jest niekompletna w odniesieniu do tego tytułu. (Jeśli chodzi o treść pytania, odpowiedź jest kompletna).
Zagnieżdżone słowniki
Jeśli ktoś szuka przepełnienia stosu, aby dowiedzieć się, jak haszować słownik, można natknąć się na to trafnie zatytułowane pytanie i pozostawić niezadowolony, jeśli ktoś próbuje haszować mnożenie zagnieżdżonych słowników. Powyższa odpowiedź nie zadziała w tym przypadku i będziesz musiał zaimplementować jakiś mechanizm rekurencyjny, aby pobrać hash.
Oto jeden taki mechanizm:
Bonus: haszowanie obiektów i klas
hash()
Funkcja działa świetnie, gdy hash klas lub instancji. Jednak tutaj jest jeden problem, który znalazłem z hashem, jeśli chodzi o obiekty:Hasz jest taki sam, nawet po zmianie foo. Dzieje się tak, ponieważ tożsamość foo się nie zmieniła, więc hash jest taki sam. Jeśli chcesz, aby foo różnie haszowało w zależności od jego aktualnej definicji, rozwiązaniem jest haszowanie tego, co faktycznie się zmienia. W tym przypadku
__dict__
atrybut:Niestety, kiedy próbujesz zrobić to samo z samą klasą:
Właściwość class
__dict__
nie jest zwykłym słownikiem:Oto podobny mechanizm jak poprzednio, który będzie odpowiednio obsługiwał klasy:
Możesz użyć tego, aby zwrócić krotkę mieszającą dowolną liczbę elementów:
UWAGA: cały powyższy kod zakłada Python 3.x. Nie testowałem we wcześniejszych wersjach, choć zakładam, że
make_hash()
będzie działać powiedzmy w 2.7.2. Jeśli chodzi o to, by przykłady działały, to wiemnależy zastąpić
źródło
hash
list wokół i krotek. W przeciwnym razie pobiera moje listy liczb całkowitych, które są wartościami w moim słowniku i zwraca listy skrótów, co nie jest tym, czego chcę.Oto jaśniejsze rozwiązanie.
źródło
if isinstance(o,list):
na,if isinstance(obj, (set, tuple, list)):
ta funkcja może działać na dowolnym obiekcie.Poniższy kod unika używania funkcji hash () w języku Python, ponieważ nie zapewnia ona skrótów spójnych podczas ponownych uruchomień Pythona (patrz funkcja skrótu w Pythonie 3.3 zwraca różne wyniki między sesjami ).
make_hashable()
skonwertuje obiekt na zagnieżdżone krotki, amake_hash_sha256()
także skonwertuje narepr()
skrót SHA256 zakodowany algorytmem base64.źródło
make_hash_sha256(((0,1),(2,3)))==make_hash_sha256({0:1,2:3})==make_hash_sha256({2:3,0:1})!=make_hash_sha256(((2,3),(0,1)))
. To nie jest rozwiązanie, którego szukam, ale jest to fajny półprodukt. Myślę o dodaniutype(o).__name__
na początku każdej z krotek, aby wymusić różnicowanie.tuple(sorted((make_hashable(e) for e in o)))
Zaktualizowano odpowiedź z 2013 roku ...
Żadna z powyższych odpowiedzi nie wydaje mi się wiarygodna. Powodem jest użycie items (). O ile wiem, pojawia się to w kolejności zależnej od maszyny.
A może zamiast tego?
źródło
dict.items
nie zwraca przewidywalnie uporządkowanej listy?frozenset
dba o tohash
nie dba o to, jak drukowana jest zawartość zamrożonego zestawu, czy coś w tym rodzaju. Przetestuj to na kilku komputerach i wersjach Pythona, a zobaczysz.Aby zachować kolejność kluczy, zamiast
hash(str(dictionary))
lubhash(json.dumps(dictionary))
wolałbym szybkie i brudne rozwiązanie:Będzie działać nawet w przypadku typów takich jak
DateTime
i innych, które nie są serializowane w formacie JSON.źródło
Możesz użyć
frozendict
modułu innej firmy , aby zamrozić swój dykt i uczynić go hashowalnym.Do obsługi obiektów zagnieżdżonych możesz użyć:
Jeśli chcesz obsługiwać więcej typów, użyj
functools.singledispatch
(Python 3.7):źródło
dict
zDataFrame
obiektów.elif
klauzulę, aby działała zDataFrame
s:elif isinstance(x, pd.DataFrame): return make_hashable(hash_pandas_object(x).tolist())
hash
randomizacja jest celową funkcją bezpieczeństwa włączoną domyślnie w Pythonie 3.7.W tym celu możesz skorzystać z biblioteki map . W szczególności mapy. FrozenMap
Aby zainstalować
maps
, po prostu wykonaj:Obsługuje również zagnieżdżony
dict
przypadek:Zastrzeżenie: jestem autorem
maps
biblioteki.źródło
.recurse
. Zobacz maps.readthedocs.io/en/latest/api.html#maps.FrozenMap.recurse . Porządkowanie w listach ma znaczenie semantyczne, jeśli chcesz niezależności kolejności, możesz przekonwertować listy na zestawy przed wywołaniem.recurse
. Można również skorzystać zlist_fn
parametru.recurse
użyć innego hashable strukturę danych niżtuple
(.egfrozenset
)Jednym ze sposobów rozwiązania problemu jest utworzenie krotki elementów słownika:
źródło
Robię to tak:
źródło
hash(str({'a': 1, 'b': 2})) != hash(str({'b': 2, 'a': 1}))
(chociaż może działać w przypadku niektórych słowników, nie gwarantuje się, że będzie działać we wszystkich).