Python hat keinen eingebauten Frozendict-Typ. Es stellt sich heraus, dass dies nicht zu oft nützlich wäre (obwohl es wahrscheinlich immer noch öfter nützlich wäre als es frozenset
ist).
Der häufigste Grund, einen solchen Typ zu wollen, besteht darin, dass beim Speichern von Funktionsfunktionen Funktionen mit unbekannten Argumenten aufgerufen werden. Die häufigste Lösung , die ein hashable Äquivalent eines dict zu speichern (wobei die Werte hashable sind) ist so etwas wie tuple(sorted(kwargs.iteritems()))
.
Dies hängt davon ab, dass die Sortierung nicht ein bisschen verrückt ist. Python kann nicht positiv versprechen, dass das Sortieren hier zu etwas Vernünftigem führt. (Aber es kann nicht viel anderes versprechen, also schwitzen Sie nicht zu viel.)
Sie könnten leicht genug eine Art Wrapper erstellen, der ähnlich wie ein Diktat funktioniert. Es könnte ungefähr so aussehen
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
Es sollte großartig funktionieren:
>>> 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__
Methode leicht verbessert werden. Verwenden Sie bei der Berechnung des Hashs einfach eine temporäre Variable und setzenself._hash
Sie diese erst, wenn Sie den endgültigen Wert haben. Auf diese Weise führt ein anderer Thread, der einen Hash erhält, während der erste berechnet, einfach eine redundante Berechnung durch, anstatt einen falschen Wert zu erhalten.Obwohl wir
frozenset
in Python selten nützlich sind , gibt es seltsamerweise immer noch keine eingefrorene Zuordnung. Die Idee wurde in PEP 416 - Hinzufügen eines eingebauten Frozendict-Typs abgelehnt . Die Idee kann in Python 3.9 überarbeitet werden, siehe PEP 603 - Hinzufügen eines Frozenmap-Typs zu Sammlungen .Also die Python 2-Lösung dafür:
Scheint immer noch etwas lahm zu sein:
In python3 haben Sie die Möglichkeit , diese :
Nun ist die Standardkonfiguration kann dynamisch aktualisiert werden, aber unveränderlich bleiben , wo Sie es möchten , indem Sie den Proxy statt , das um unveränderlich zu sein.
Änderungen im
default_config
werden alsoDEFAULTS
wie erwartet aktualisiert , aber Sie können nicht in das Mapping-Proxy-Objekt selbst schreiben.Zugegeben, es ist nicht ganz dasselbe wie ein "unveränderliches, hashbares Diktat" - aber es ist ein anständiger Ersatz angesichts der gleichen Art von Anwendungsfällen, für die wir vielleicht ein Frozendict wollen.
quelle
def foo(config=MappingProxyType({'a': 1})):
? Ihr Beispiel erlaubt auch weiterhin globale Änderungendefault_config
.config = default_config = {'a': 1}
ein Tippfehler ist.Angenommen, die Schlüssel und Werte des Wörterbuchs sind selbst unveränderlich (z. B. Zeichenfolgen), dann:
quelle
dict(t)
Es gibt keine
fronzedict
, aber Sie können diese verwendenMappingProxyType
, die der Standardbibliothek mit Python 3.3 hinzugefügt wurde:quelle
TypeError: can't pickle mappingproxy objects
Hier ist der Code, den ich verwendet habe. Ich habe Frozenset untergeordnet. Dies hat folgende Vorteile.
Update 21. Januar 2015: Der ursprüngliche Code, den ich 2014 veröffentlicht habe, verwendete eine for-Schleife, um einen passenden Schlüssel zu finden. Das war unglaublich langsam. Jetzt habe ich eine Implementierung zusammengestellt, die die Hashing-Funktionen von frozenset nutzt. Schlüssel-Wert-Paare werden in speziellen Containern gespeichert, in denen die Funktionen
__hash__
und__eq__
nur auf dem Schlüssel basieren. Dieser Code wurde im Gegensatz zu dem, was ich hier im August 2014 gepostet habe, auch offiziell einem Unit-Test unterzogen.MIT-Lizenz.
quelle
Item
, um der Hash des Schlüssels zu sein, ist ein ordentlicher Hack!diff(diff({key}))
in der Größe des FrozenDict immer noch linear, während die reguläre Zugriffszeit für Diktate im Durchschnitt konstant ist.Ich denke jedes Mal an frozendict, wenn ich eine Funktion wie diese schreibe:
quelle
optional_dict_parm = optional_dict_parm or {}
types.MappingProxyType
({})
Sie können
frozendict
aus demutilspie
Paket verwenden als:Gemäß dem Dokument :
quelle
Installieren Sie frozendict
Benutze es!
quelle
Ja, dies ist meine zweite Antwort, aber es ist ein völlig anderer Ansatz. Die erste Implementierung war in reinem Python. Dieser ist in Cython. Wenn Sie wissen, wie man Cython-Module verwendet und kompiliert, ist dies genauso schnell wie ein normales Wörterbuch. Etwa 0,04 bis 0,06 Mikrosekunden, um einen einzelnen Wert abzurufen.
Dies ist die Datei "Frozen_Dict.pyx"
Hier ist die Datei "setup.py"
Wenn Sie Cython installiert haben, speichern Sie die beiden oben genannten Dateien im selben Verzeichnis. Wechseln Sie in dieses Verzeichnis in der Befehlszeile.
Und du solltest fertig sein.
quelle
Der Hauptnachteil von
namedtuple
ist, dass es spezifiziert werden muss, bevor es verwendet wird, so dass es für Einzelanwendungsfälle weniger bequem ist.Es gibt jedoch eine praktische Problemumgehung, mit der viele solcher Fälle behandelt werden können. Angenommen, Sie möchten ein unveränderliches Äquivalent des folgenden Diktats haben:
Dies kann folgendermaßen emuliert werden:
Es ist sogar möglich, eine Hilfsfunktion zu schreiben, um dies zu automatisieren:
Natürlich funktioniert dies nur für flache Diktate, aber es sollte nicht zu schwierig sein, eine rekursive Version zu implementieren.
quelle
getattr(fa, x)
stattdessenfa[x]
keinekeys
Methode an Ihren Fingerspitzen ausführen, und alle anderen Gründe, aus denen eine Zuordnung wünschenswert sein kann.Unterklasse
dict
Ich sehe dieses Muster in freier Wildbahn (Github) und wollte es erwähnen:
Anwendungsbeispiel:
Vorteile
get()
,keys()
,items()
(iteritems()
auf py2) und alle Leckereien ausdict
aus dem Kasten heraus, ohne sie explizit die Umsetzungdict
was Leistung bedeutet (dict
wird in CPython in c geschrieben)isinstance(my_frozen_dict, dict)
Gibt True zurück - obwohl Python die Enten-Typisierung bei vielen Paketen fördertisinstance()
, können dadurch viele Optimierungen und Anpassungen eingespart werdenNachteile
__hash__
etwas schneller machen.quelle
__setitem__
und Erbendict
im Vergleich zu vielen Alternativen wahnsinnig schnell ist.Eine weitere Option ist die
MultiDictProxy
Klasse aus demmultidict
Paket.quelle
Ich musste an einem Punkt auf feste Schlüssel für etwas zugreifen, für etwas, das eine Art global konstante Sache war, und ich entschied mich für so etwas:
Verwenden Sie es wie
WARNUNG: Ich empfehle dies nicht für die meisten Anwendungsfälle, da es einige ziemlich schwerwiegende Kompromisse macht.
quelle
Wenn keine Unterstützung für Muttersprachen verfügbar ist, können Sie dies entweder selbst tun oder eine vorhandene Lösung verwenden. Glücklicherweise macht es Python ganz einfach, die Basisimplementierungen zu erweitern.
quelle