PEP 416 – Hinzufügen eines eingebauten Typs frozendict
- Autor:
- Victor Stinner <vstinner at python.org>
- Status:
- Abgelehnt
- Typ:
- Standards Track
- Erstellt:
- 29. Feb. 2012
- Python-Version:
- 3.3
Ablehnungsbescheid
Ich lehne diese PEP ab. Aus einer Reihe von Gründen (nicht erschöpfend)
- Laut Raymond Hettinger ist die Verwendung von frozendict gering. Diejenigen, die es verwenden, tun dies tendenziell nur als Hinweis, z. B. durch Deklarieren von globalen oder klassenweiten „Konstanten“: Sie sind nicht wirklich unveränderlich, da jeder immer noch dem Namen zuweisen kann.
- Es gibt bestehende Idiome, um veränderliche Standardwerte zu vermeiden.
- Das Potenzial, Code mit frozendict in PyPy zu optimieren, ist ungewiss; viele andere Dinge müssten sich zuerst ändern. Dasselbe gilt für Compile-Time-Lookups im Allgemeinen.
- Mehrere Threads können sich durch Konvention darauf einigen, ein gemeinsames Dict nicht zu verändern, es gibt keinen großen Bedarf an Durchsetzung. Mehrere Prozesse können keine Dictionaries gemeinsam nutzen.
- Das Hinzufügen einer in Python geschriebenen Sicherheits-Sandbox wird von vielen missbilligt, da es inhärent schwierig ist, jemals zu beweisen, dass die Sandbox tatsächlich sicher ist. Aus diesem Grund werden wir keine in die Standardbibliothek aufnehmen, daher fällt dieser Anwendungsfall außerhalb des Rahmens einer PEP.
Andererseits klingt die Offenlegung des bestehenden schreibgeschützten Dict-Proxys als integrierter Typ gut. (Es müsste geändert werden, um den Aufruf des Konstruktors zu ermöglichen.) GvR.
Update (15.04.2012): Ein neuer Typ MappingProxyType wurde dem types-Modul von Python 3.3 hinzugefügt.
Zusammenfassung
Fügen Sie einen neuen eingebauten Typ frozendict hinzu.
Begründung
Ein frozendict ist eine schreibgeschützte Zuordnung: Ein Schlüssel kann nicht hinzugefügt oder entfernt werden, und ein Schlüssel wird immer demselben Wert zugeordnet. frozendict-Werte können jedoch nicht hashbar sein. Ein frozendict ist genau dann hashbar, wenn alle Werte hashbar sind.
Anwendungsfälle
- Unveränderliche globale Variable wie eine Standardkonfiguration.
- Standardwert eines Funktionsparameters. Vermeiden Sie das Problem von veränderlichen Standardargumenten.
- Implementieren Sie einen Cache: frozendict kann verwendet werden, um Funktionsschlüsselwörter zu speichern. frozendict kann als Schlüssel einer Zuordnung oder als Element einer Menge verwendet werden.
- frozendict vermeidet die Notwendigkeit eines Locks, wenn das frozendict von mehreren Threads oder Prozessen gemeinsam genutzt wird, insbesondere ein hashbares frozendict. Es würde auch helfen, Coroutinen (Generatoren + Greenlets) daran zu hindern, den globalen Zustand zu ändern.
- frozendict-Lookup kann zur Kompilierungszeit anstelle der Laufzeit erfolgen, da die Zuordnung schreibgeschützt ist. frozendict kann anstelle eines Präprozessors verwendet werden, um bedingten Code zur Kompilierungszeit zu entfernen, wie z. B. Code, der für einen Debug-Build spezifisch ist.
- frozendict hilft bei der Implementierung von schreibgeschützten Objekt-Proxys für Sicherheitsmodule. Zum Beispiel wäre es möglich, den Typ frozendict für die Zuordnung __builtins__ oder type.__dict__ zu verwenden. Dies ist möglich, da frozendict mit der PyDict C API kompatibel ist.
- frozendict vermeidet in einigen Fällen die Notwendigkeit eines schreibgeschützten Proxys. frozendict ist schneller als ein Proxy, da der Abruf eines Elements in einem frozendict ein schneller Lookup ist, während ein Proxy einen Funktionsaufruf erfordert.
Beschränkungen
- frozendict muss die Mapping-Abstrakte Basisklasse implementieren
- frozendict-Schlüssel und -Werte können nicht sortierbar sein
- ein frozendict ist hashbar, wenn alle Schlüssel und Werte hashbar sind
- frozendict-Hash hängt nicht von der Reihenfolge der Elementerstellung ab
Implementierung
- Fügen Sie eine PyFrozenDictObject-Struktur basierend auf PyDictObject mit einem zusätzlichen Feld „Py_hash_t hash;“ hinzu
- frozendict.__hash__() wird mit hash(frozenset(self.items())) implementiert und speichert das Ergebnis in seinem privaten Hash-Attribut
- Registrieren Sie frozendict als collections.abc.Mapping
- frozendict kann mit PyDict_GetItem() verwendet werden, aber PyDict_SetItem() und PyDict_DelItem() lösen einen TypeError aus
Rezept: Hashable Dict
Um sicherzustellen, dass ein frozendict hashbar ist, können die Werte vor der Erstellung des frozendict überprüft werden
import itertools
def hashabledict(*args, **kw):
# ensure that all values are hashable
for key, value in itertools.chain(args, kw.items()):
if isinstance(value, (int, str, bytes, float, frozenset, complex)):
# avoid the compute the hash (which may be slow) for builtin
# types known to be hashable for any value
continue
hash(value)
# don't check the key: frozendict already checks the key
return frozendict.__new__(cls, *args, **kw)
Einwände
namedtuple kann die Anforderungen eines frozendict erfüllen.
Ein namedtuple ist keine Zuordnung, es implementiert nicht die Mapping-Abstrakte Basisklasse.
frozendict kann in Python mit Deskriptoren implementiert werden" und "frozendict muss nur praktisch konstant sein.
Wenn frozendict zur Härtung von Python (zu Sicherheitszwecken) verwendet wird, muss es in C implementiert werden. Ein in C implementierter Typ ist auch schneller.
Die PEP 351 wurde abgelehnt.
Die PEP 351 versucht, ein Objekt einzufrieren und kann daher ein veränderliches Objekt in ein unveränderliches Objekt umwandeln (unter Verwendung eines anderen Typs). frozendict wandelt nichts um: hash(frozendict) löst einen TypeError aus, wenn ein Wert nicht hashbar ist. Das Einfrieren eines Objekts ist nicht der Zweck dieser PEP.
Alternative: dictproxy
Python hat einen eingebauten dictproxy-Typ, der vom type.__dict__ Getter-Deskriptor verwendet wird. Dieser Typ ist nicht öffentlich. dictproxy ist eine schreibgeschützte Ansicht eines Dictionaries, aber es ist keine schreibgeschützte Zuordnung. Wenn ein Dictionary geändert wird, wird auch das dictproxy geändert.
dictproxy kann über ctypes und die Python C API verwendet werden, siehe zum Beispiel das Erstellen eines dictproxy-Objekts über ctypes.pythonapi und type() (Python-Rezept 576540) von Ikkei Shimomura. Das Rezept enthält einen Test, der prüft, ob ein dictproxy „veränderlich“ ist (das mit dem dictproxy verknüpfte Dictionary ändern).
dictproxy kann jedoch in einigen Fällen nützlich sein, in denen seine veränderliche Eigenschaft kein Problem darstellt, um eine Kopie des Dictionaries zu vermeiden.
Bestehende Implementierungen
Whitelist-Ansatz.
- Implementierung eines unveränderlichen Dictionaries (Python-Rezept 498072) von Aristotelis Mikropoulos. Ähnlich wie frozendict, außer dass es nicht wirklich schreibgeschützt ist: Es ist möglich, auf dieses private interne Dict zuzugreifen. Es implementiert __hash__ nicht und hat ein Implementierungsproblem: Es ist möglich, __init__() erneut aufzurufen, um die Zuordnung zu ändern.
- PyWebmail enthält einen ImmutableDict-Typ: webmail.utils.ImmutableDict. Es ist hashbar, wenn Schlüssel und Werte hashbar sind. Es ist nicht wirklich schreibgeschützt: Sein internes Dict ist ein öffentliches Attribut.
- remember Projekt: remember.dicts.FrozenDict. Es wird zur Implementierung eines Caches verwendet: FrozenDict wird zum Speichern von Funktions-Callbacks verwendet. FrozenDict kann hashbar sein. Es hat eine zusätzliche Klassenmethode supply_dict(), um ein FrozenDict aus einem Dict ohne Kopieren des Dicts zu erstellen: Speichert das Dict als internes Dict. Implementierungsproblem: __init__() kann aufgerufen werden, um die Zuordnung zu ändern, und der Hash kann sich je nach Reihenfolge der Elementerstellung unterscheiden. Die Zuordnung ist nicht wirklich schreibgeschützt: Das interne Dict ist in Python zugänglich.
Blacklist-Ansatz: von dict erben und Schreibmethoden überschreiben, um eine Ausnahme auszulösen. Es ist nicht wirklich schreibgeschützt: Es ist immer noch möglich, dict-Methoden für ein solches „eingefrorenes Dictionary“ aufzurufen, um es zu ändern.
- brownie: brownie.datastructures.ImmutableDict. Es ist hashbar, wenn Schlüssel und Werte hashbar sind. Das werkzeug-Projekt hat denselben Code: werkzeug.datastructures.ImmutableDict. ImmutableDict wird für globale Konstanten (Konfigurationsoptionen) verwendet. Das Flask-Projekt verwendet ImmutableDict von werkzeug für seine Standardkonfiguration.
- SQLAlchemy-Projekt: sqlalchemy.util.immutabledict. Es ist nicht hashbar und hat eine zusätzliche Methode: union(). immutabledict wird für den Standardwert von Parametern einiger Funktionen verwendet, die eine Zuordnung erwarten. Beispiel: mapper_args=immutabledict() in SqlSoup.map().
- Eingefrorene Dictionaries (Python-Rezept 414283) von Oren Tirosh. Es ist hashbar, wenn Schlüssel und Werte hashbar sind. Enthalten in den folgenden Projekten
- lingospot: frozendict/frozendict.py
- factor-graphics: frozendict-Typ in python/fglib/util_ext_frozendict.py
- Das von George Sakkis geschriebene gsakkis-utils Projekt enthält einen frozendict-Typ: datastructs.frozendict
- characters: scripts/python/frozendict.py. Es ist hashbar. __init__() setzt __init__ auf None.
- Altes NLTK (1.x): nltk.util.frozendict. Schlüssel und Werte müssen hashbar sein. __init__() kann zweimal aufgerufen werden, um die Zuordnung zu ändern. frozendict wird verwendet, um ein Objekt zu „frieren“.
Hashable dict: von dict erben und nur eine __hash__ Methode hinzufügen.
- pypy.rpython.lltypesystem.lltype.frozendict. Es ist hashbar, verweigert aber nicht die Änderung der Zuordnung.
- factor-graphics: hashabledict-Typ in python/fglib/util_ext_frozendict.py
Links
- Problem #14162: PEP 416: Hinzufügen eines integrierten frozendict-Typs
- PEP 412: Key-Sharing Dictionary (Problem #13903)
- PEP 351: Das Freeze-Protokoll
- Die Argumente für unveränderliche Dictionaries; und das zentrale Missverständnis von PEP 351
- Erstellen eines dictproxy-Objekts über ctypes.pythonapi und type() (Python-Rezept 576540) von Ikkei Shimomura.
- Python-Sicherheitsmodule, die schreibgeschützte Objekt-Proxys mithilfe einer C-Erweiterung implementieren
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0416.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT