Python nie ma wbudowanego typu Frozendict. Okazuje się, że nie byłoby to przydatne zbyt często (chociaż prawdopodobnie byłoby to przydatne częściej niż frozenset
jest).
Najczęstszym powodem, dla którego warto chcieć takiego typu jest zapamiętywanie wywołań funkcji z nieznanymi argumentami. Najczęstszym rozwiązaniem do przechowywania hashowalnego odpowiednika dyktu (gdzie wartości są hashowalne) jest coś takiego tuple(sorted(kwargs.iteritems()))
.
To zależy od tego, czy sortowanie nie jest trochę szalone. Python nie może obiecać, że sortowanie przyniesie tutaj coś rozsądnego. (Ale nie może obiecać nic więcej, więc nie przejmuj się zbytnio).
Możesz łatwo stworzyć jakiś rodzaj opakowania, które działa podobnie jak dykt. To może wyglądać jak
import collections
class FrozenDict(collections.Mapping):
"""Don't forget the docstrings!!"""
def __init__(self, *args, **kwargs):
self._d = dict(*args, **kwargs)
self._hash = None
def __iter__(self):
return iter(self._d)
def __len__(self):
return len(self._d)
def __getitem__(self, key):
return self._d[key]
def __hash__(self):
# It would have been simpler and maybe more obvious to
# use hash(tuple(sorted(self._d.iteritems()))) from this discussion
# so far, but this solution is O(n). I don't know what kind of
# n we are going to run into, but sometimes it's hard to resist the
# urge to optimize when it will gain improved algorithmic performance.
if self._hash is None:
hash_ = 0
for pair in self.items():
hash_ ^= hash(pair)
self._hash = hash_
return self._hash
Powinno działać świetnie:
>>> x = FrozenDict(a=1, b=2)
>>> y = FrozenDict(a=1, b=2)
>>> x is y
False
>>> x == y
True
>>> x == {'a': 1, 'b': 2}
True
>>> d = {x: 'foo'}
>>> d[y]
'foo'
__hash__
metoda mogłaby zostać nieco ulepszona. Po prostu użyj zmiennej tymczasowej podczas obliczania skrótu i ustaw ją dopieroself._hash
po uzyskaniu ostatecznej wartości. W ten sposób inny wątek otrzymujący hash podczas obliczania pierwszego po prostu wykona zbędne obliczenia, zamiast uzyskać nieprawidłową wartość.Co ciekawe, chociaż rzadko mamy przydatne
frozenset
w Pythonie, nadal nie ma zamrożonego mapowania. Pomysł został odrzucony w PEP 416 - Dodaj wbudowany typ frozendict . Pomysł może zostać ponownie przeanalizowany w Pythonie 3.9, zobacz PEP 603 - Dodawanie typu frozenmap do kolekcji .Więc rozwiązanie Pythona 2 do tego:
Nadal wydaje się być nieco kulawy:
W python3 masz możliwość to :
Teraz domyślna konfiguracja może być aktualizowana dynamicznie, ale pozostaje niezmienna tam, gdzie ma być niezmienna, zamiast tego przekazując serwer proxy.
Zatem zmiany w
default_config
aktualizacji zostaną zaktualizowaneDEFAULTS
zgodnie z oczekiwaniami, ale nie można pisać do samego obiektu proxy mapowania.Wprawdzie to nie to samo, co „niezmienny, haszowalny dykt” - ale jest to przyzwoity substytut, biorąc pod uwagę te same przypadki użycia, dla których moglibyśmy chcieć zamrożonego zakończenia.
źródło
def foo(config=MappingProxyType({'a': 1})):
? Twój przykład nadal umożliwia również globalną modyfikacjędefault_config
.config = default_config = {'a': 1}
to błąd.Zakładając, że klucze i wartości słownika są same w sobie niezmienne (np. Stringi) to:
źródło
dict(t)
Nie ma
fronzedict
, ale możesz użyćMappingProxyType
tego, co zostało dodane do standardowej biblioteki w Pythonie 3.3:źródło
TypeError: can't pickle mappingproxy objects
Oto kod, którego używałem. Podklasa zamrożona. Zalety tego są następujące.
Aktualizacja, 21 stycznia 2015 r .: Oryginalny fragment kodu, który opublikowałem w 2014 r., Używał pętli for, aby znaleźć pasujący klucz. To było niesamowicie powolne. Teraz przygotowałem implementację, która korzysta z funkcji mieszania Frozenset. Pary klucz-wartość są przechowywane w specjalnych kontenerach, w których
__hash__
i__eq__
funkcje są oparte tylko na kluczu. Ten kod został również formalnie przetestowany jednostkowo, w przeciwieństwie do tego, co opublikowałem tutaj w sierpniu 2014 r.Licencja typu MIT.
źródło
Item
klucza jako skrótu klucza to zgrabny hack!diff(diff({key}))
nadal jest liniowy w stosunku do rozmiaru FrozenDict, podczas gdy zwykły czas dostępu do dyktowania jest stały w przeciętnym przypadku.Myślę o frozendict za każdym razem, gdy piszę taką funkcję:
źródło
optional_dict_parm = optional_dict_parm or {}
types.MappingProxyType
({})
Możesz użyć
frozendict
zutilspie
pakietu jako:Zgodnie z dokumentem :
źródło
Zainstaluj frozendict
Użyj tego!
źródło
Tak, to moja druga odpowiedź, ale to zupełnie inne podejście. Pierwsza implementacja była w czystym Pythonie. Ten jest w Cythonie. Jeśli wiesz, jak używać i kompilować moduły Cython, jest to tak samo szybkie, jak zwykły słownik. Około 0,04 do 0,06 mikrosekundy, aby pobrać pojedynczą wartość.
To jest plik „frozen_dict.pyx”
Oto plik „setup.py”
Jeśli masz zainstalowany Cython, zapisz dwa powyższe pliki w tym samym katalogu. Przejdź do tego katalogu w wierszu poleceń.
I powinno być gotowe.
źródło
Główną wadą programu
namedtuple
jest to, że należy go określić przed użyciem, więc jest mniej wygodny w przypadkach jednorazowego użytku.Istnieje jednak praktyczne obejście, które można zastosować w wielu takich przypadkach. Załóżmy, że chcesz mieć niezmienny odpowiednik następującego dyktowania:
Można to emulować w następujący sposób:
Możliwe jest nawet napisanie funkcji pomocniczej, aby zautomatyzować to:
Oczywiście działa to tylko dla płaskich dykt, ale implementacja wersji rekurencyjnej nie powinna być zbyt trudna.
źródło
getattr(fa, x)
zamiast tego musisz zrobićfa[x]
, żadnejkeys
metody na wyciągnięcie ręki, a wszystkie inne powody, dla których mapowanie może być pożądane.Podklasy
dict
widzę ten wzór na wolności (github) i chciałem o nim wspomnieć:
przykład użycia:
Plusy
get()
,keys()
,items()
(iteritems()
na Py2) i wszystkie gadżety zdict
wyjęciu z pudełka, bez wyraźnego ich realizacjidict
co oznacza wydajność (dict
jest napisane w c w CPythonie)isinstance(my_frozen_dict, dict)
zwraca True - chociaż Python zachęca do pisania kaczkami w wielu pakietachisinstance()
, może to zaoszczędzić wiele poprawek i dostosowańCons
__hash__
trochę przyspieszyć.źródło
__setitem__
i dziedziczeniedict
jest niesamowicie szybkie w porównaniu z wieloma alternatywami.Inną opcją jest
MultiDictProxy
klasa zmultidict
pakietu.źródło
W pewnym momencie musiałem uzyskać dostęp do stałych kluczy do czegoś, co było czymś w rodzaju globalnej trwałości i zdecydowałem się na coś takiego:
Użyj tego jak
OSTRZEŻENIE: Nie polecam tego w większości przypadków użycia, ponieważ powoduje to dość poważne kompromisy.
źródło
W przypadku braku obsługi języka ojczystego możesz to zrobić samodzielnie lub skorzystać z istniejącego rozwiązania. Na szczęście Python sprawia, że rozszerzenie ich podstawowych implementacji jest bardzo proste.
źródło