Was wäre ein “eingefrorenes Diktat”?

Lesezeit: 13 Minuten

Was ware ein eingefrorenes Diktat
dugres

  • Ein eingefrorenes Set ist ein eingefrorenes Set.
  • Eine eingefrorene Liste könnte ein Tupel sein.
  • Was wäre ein eingefrorenes Diktat? Ein unveränderliches, hashbares Diktat.

Ich denke, es könnte so etwas sein collections.namedtuple, aber das ist eher ein Diktat mit eingefrorenen Schlüsseln (ein halb eingefrorenes Diktat). Ist es nicht?

Ein “eingefrorenes Wörterbuch” sollte ein eingefrorenes Wörterbuch sein, sollte es haben keys, values, getusw., und Unterstützung in, foretc.

aktualisieren :
* da ist es : https://www.python.org/dev/peps/pep-0603

Was ware ein eingefrorenes Diktat
Mike Graham

Python hat keinen eingebauten Frozendict-Typ. Es stellt sich heraus, dass dies nicht allzu oft nützlich wäre (obwohl es wahrscheinlich immer noch öfter als nützlich wäre frozenset ist).

Der häufigste Grund für einen solchen Typ ist das Merken von Funktionsaufrufen für Funktionen mit unbekannten Argumenten. Die gebräuchlichste Lösung zum Speichern eines hashbaren Äquivalents eines Diktats (wobei die Werte hashbar sind) ist so etwas wie tuple(sorted(kwargs.iteritems())).

Dies hängt davon ab, ob 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 mehr versprechen, also schwitzen Sie nicht zu sehr.)


Sie könnten leicht genug eine Art Wrapper erstellen, der ähnlich wie ein Diktat funktioniert. Es könnte 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 super 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'

  • Ich weiß nicht, über welches Maß an Thread-Sicherheit sich die Leute bei so etwas Sorgen machen, aber in dieser Hinsicht Ihre __hash__ Methode könnte etwas verbessert werden. Verwenden Sie einfach eine temporäre Variable, wenn Sie den Hash berechnen, und setzen Sie sie nur self._hash Sobald 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.

    – Jeff DQ

    2. Juni 11 um 17:02 Uhr

  • @Jeff In der Regel ist der gesamte Code nicht überall Thread-sicher, und Sie sollten ihn um einige Synchronisierungsstrukturen wickeln, um diesen Code sicher zu verwenden. Außerdem beruht Ihre spezielle Vorstellung von Thread-Sicherheit auf der Atomarität der Objektattributzuweisung, die bei weitem nicht garantiert ist.

    – Devin Jeanpierre

    3. Juni 11 um 1:07 Uhr

  • @Anentropic, das stimmt überhaupt nicht.

    – Mike Graham

    10. November 11 um 14:08 Uhr

  • Seien Sie gewarnt: Dieses “FrozenDict” ist nicht unbedingt eingefroren. Nichts hindert Sie daran, eine veränderliche Liste als Wert zu setzen, in diesem Fall wird Hashing einen Fehler ausgeben. Daran ist nicht unbedingt etwas auszusetzen, aber die Benutzer sollten sich dessen bewusst sein. Eine andere Sache: Dieser Hash-Algorithmus ist schlecht gewählt, sehr anfällig für Hash-Kollisionen. Zum Beispiel hasht {‘a’:’b’} dasselbe wie {‘b’:’a’} und {‘a’:1, ‘b’:2} hasht dasselbe wie {‘a’:2, ‘ b’:1}. Besser wäre self._hash ^= hash((key, value))

    – Steve Byrnes

    11. November 12 um 18:20 Uhr


  • Wenn Sie einem unveränderlichen Objekt einen veränderlichen Eintrag hinzufügen, sind die beiden möglichen Verhaltensweisen, einen Fehler beim Erstellen des Objekts auszugeben oder einen Fehler beim Hashen des Objekts auszulösen. Tupel machen das letztere, Frozenset macht das erstere. Ich denke definitiv, dass Sie eine gute Entscheidung getroffen haben, den letzteren Ansatz zu wählen, alles in allem. Trotzdem denke ich, dass die Leute sehen könnten, dass FrozenDict und Frozenset ähnliche Namen haben, und voreilig zu dem Schluss kommen, dass sie sich ähnlich verhalten sollten. Ich denke also, es lohnt sich, die Leute vor diesem Unterschied zu warnen. 🙂

    – Steve Byrnes

    11. November 12 um 19:52 Uhr

Was ware ein eingefrorenes Diktat
Wim

Kurioserweise haben wir das zwar selten brauchbar frozenset, gibt es immer noch keine eingefrorene Zuordnung. Die Idee wurde verworfen PEP 416 – Fügt einen integrierten Frozendict-Typ hinzu. Diese Idee kann in einer späteren Python-Version wieder aufgegriffen werden, siehe PEP 603 – Hinzufügen eines eingefrorenen Kartentyps zu Sammlungen.

Also die Python 2-Lösung dazu:

def foo(config={'a': 1}):
    ...

Scheint noch etwas lahm zu sein:

def foo(config=None):
    if config is None:
        config = default_config = {'a': 1}
    ...

In Python 3 haben Sie die Möglichkeit Dies:

from types import MappingProxyType

default_config = {'a': 1}
DEFAULTS = MappingProxyType(default_config)

def foo(config=DEFAULTS):
    ...

Jetzt die Standardkonfiguration kann dynamisch aktualisiert werden, aber dort unveränderlich bleiben, wo Sie es unveränderlich haben möchten, indem Sie stattdessen den Proxy herumreichen.

Also Veränderungen in der default_config werde dich auf den neuesten Stand bringen DEFAULTS wie erwartet, aber Sie können nicht in das Mapping-Proxy-Objekt selbst schreiben.

Zugegeben, es ist nicht wirklich dasselbe wie ein “unveränderliches, hashbares Diktat”, aber es könnte ein anständiger Ersatz für einige Anwendungsfälle eines eingefrorenen Diktats sein.

  • Gibt es einen besonderen Grund, den Proxy in einer Modulvariablen zu speichern? Warum nicht einfach def foo(config=MappingProxyType({'a': 1})):? Ihr Beispiel lässt weiterhin globale Änderungen zu default_configauch.

    – jpmc26

    26. Mai ’18 um 3:20 Uhr


  • Außerdem vermute ich die Doppelbelegung in config = default_config = {'a': 1} ist ein Tippfehler.

    – jpmc26

    26. Mai 18 um 3:21 Uhr

Angenommen, die Schlüssel und Werte des Wörterbuchs sind selbst unveränderlich (z. B. Zeichenfolgen), dann:

>>> d
{'forever': 'atones', 'minks': 'cards', 'overhands': 'warranted', 
 'hardhearted': 'tartly', 'gradations': 'snorkeled'}
>>> t = tuple((k, d[k]) for k in sorted(d.keys()))
>>> hash
1524953596

  • Dies ist eine gute, kanonische, unveränderliche Darstellung eines Diktats (abgesehen von wahnsinnigem Vergleichsverhalten, das die Sortierung durcheinander bringt).

    – Mike Graham

    24. April 10 um 16:06 Uhr

  • @devin: voll und ganz einverstanden, aber ich lasse meinen Beitrag mal als Beispiel stehen, dass es oft noch besser geht.

    – msw

    9. Mai 10 um 0:03 Uhr

  • Noch besser wäre es, es in ein eingefrorenes Set zu stecken, das keine konsistente Reihenfolge der Schlüssel oder Werte erfordert.

    – asmeurer

    22. Mai ’12 um 22:09 Uhr

  • Nur ein Problem dabei: Sie haben kein Mapping mehr. Das wäre der springende Punkt, wenn man überhaupt das eingefrorene Diktat hätte.

    – Verrückter Physiker

    29. Juli 16 um 13:19 Uhr

  • Diese Methode ist wirklich gut, wenn Sie zu einem Diktat zurückkehren. einfach dict

    – codythecoder

    14. August 18 um 2:13 Uhr

Was ware ein eingefrorenes Diktat
Julio Marins

Es gibt kein fronzedictaber Sie können verwenden MappingProxyType die mit Python 3.3 zur Standardbibliothek hinzugefügt wurde:

>>> from types import MappingProxyType
>>> foo = MappingProxyType({'a': 1})
>>> foo
mappingproxy({'a': 1})
>>> foo['a'] = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> foo
mappingproxy({'a': 1})

Ich denke jedes Mal an Frozendict, wenn ich eine Funktion wie diese schreibe:

def do_something(blah, optional_dict_parm=None):
    if optional_dict_parm is None:
        optional_dict_parm = {}

  • Jedes Mal, wenn ich einen Kommentar wie diesen sehe, bin ich mir sicher, dass ich irgendwo einen Fehler gemacht und {} als Standard gesetzt habe, und gehe zurück und schaue mir meinen kürzlich geschriebenen Code an.

    – Ryan Hiebert

    27. Dezember 2011 um 23:30 Uhr

  • Ja, es ist ein böser Fallstrick, auf den jeder früher oder später stößt.

    – Markus Visser

    5. Januar 12 um 20:59 Uhr

  • Einfachere Formulierung: optional_dict_parm = optional_dict_parm or {}

    – Emmanuel

    13. Juni 12 um 12:08 Uhr

  • In diesem Fall können Sie verwenden types.MappingProxyType({}) als Standardwert für Argument.

    – IngwerPlusPlus

    10. April 15 um 19:50 Uhr

  • @Emmanuel Du willst das is None prüfen, um falsche Argumente abzufangen, wie zum Beispiel MappingProxyType({})oder wenn jemand einen Tippfehler gemacht hat, 0 zum Beispiel.

    – wjandrea

    2. November 21 um 20:38 Uhr

1643905570 118 Was ware ein eingefrorenes Diktat
IngwerPlusPlus

Hier ist der Code, den ich verwendet habe. Ich habe Frozenset unterklassiert. Die Vorteile davon sind die folgenden.

  1. Dies ist ein wirklich unveränderliches Objekt. Verlassen Sie sich nicht auf das gute Verhalten zukünftiger Benutzer und Entwickler.
  2. Es ist einfach, zwischen einem normalen Wörterbuch und einem eingefrorenen Wörterbuch hin und her zu konvertieren. FrozenDict(orig_dict) --> eingefrorenes Wörterbuch. dict(frozen_dict) --> normales dict.

Update vom 21. Januar 2015: Der ursprüngliche Code, den ich 2014 gepostet 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 __hash__ und __eq__ Funktionen basieren nur auf dem Schlüssel. Dieser Code wurde im Gegensatz zu dem, was ich hier im August 2014 gepostet habe, auch offiziell Unit-Tested.

Lizenz im MIT-Stil.

if 3 / 2 == 1:
    version = 2
elif 3 / 2 == 1.5:
    version = 3

def col(i):
    ''' For binding named attributes to spots inside subclasses of tuple.'''
    g = tuple.__getitem__
    @property
    def _col(self):
        return g(self,i)
    return _col

class Item(tuple):
    ''' Designed for storing key-value pairs inside
        a FrozenDict, which itself is a subclass of frozenset.
        The __hash__ is overloaded to return the hash of only the key.
        __eq__ is overloaded so that normally it only checks whether the Item's
        key is equal to the other object, HOWEVER, if the other object itself
        is an instance of Item, it checks BOTH the key and value for equality.

        WARNING: Do not use this class for any purpose other than to contain
        key value pairs inside FrozenDict!!!!

        The __eq__ operator is overloaded in such a way that it violates a
        fundamental property of mathematics. That property, which says that
        a == b and b == c implies a == c, does not hold for this object.
        Here's a demonstration:
            [in]  >>> x = Item(('a',4))
            [in]  >>> y = Item(('a',5))
            [in]  >>> hash('a')
            [out] >>> 194817700
            [in]  >>> hash(x)
            [out] >>> 194817700
            [in]  >>> hash(y)
            [out] >>> 194817700
            [in]  >>> 'a' == x
            [out] >>> True
            [in]  >>> 'a' == y
            [out] >>> True
            [in]  >>> x == y
            [out] >>> False
    '''

    __slots__ = ()
    key, value = col(0), col(1)
    def __hash__(self):
        return hash(self.key)
    def __eq__(self, other):
        if isinstance(other, Item):
            return tuple.__eq__(self, other)
        return self.key == other
    def __ne__(self, other):
        return not self.__eq__(other)
    def __str__(self):
        return '%r: %r' % self
    def __repr__(self):
        return 'Item((%r, %r))' % self

class FrozenDict(frozenset):
    ''' Behaves in most ways like a regular dictionary, except that it's immutable.
        It differs from other implementations because it doesn't subclass "dict".
        Instead it subclasses "frozenset" which guarantees immutability.
        FrozenDict instances are created with the same arguments used to initialize
        regular dictionaries, and has all the same methods.
            [in]  >>> f = FrozenDict(x=3,y=4,z=5)
            [in]  >>> f['x']
            [out] >>> 3
            [in]  >>> f['a'] = 0
            [out] >>> TypeError: 'FrozenDict' object does not support item assignment

        FrozenDict can accept un-hashable values, but FrozenDict is only hashable if its values are hashable.
            [in]  >>> f = FrozenDict(x=3,y=4,z=5)
            [in]  >>> hash(f)
            [out] >>> 646626455
            [in]  >>> g = FrozenDict(x=3,y=4,z=[])
            [in]  >>> hash(g)
            [out] >>> TypeError: unhashable type: 'list'

        FrozenDict interacts with dictionary objects as though it were a dict itself.
            [in]  >>> original = dict(x=3,y=4,z=5)
            [in]  >>> frozen = FrozenDict(x=3,y=4,z=5)
            [in]  >>> original == frozen
            [out] >>> True

        FrozenDict supports bi-directional conversions with regular dictionaries.
            [in]  >>> original = {'x': 3, 'y': 4, 'z': 5}
            [in]  >>> FrozenDict(original)
            [out] >>> FrozenDict({'x': 3, 'y': 4, 'z': 5})
            [in]  >>> dict(FrozenDict(original))
            [out] >>> {'x': 3, 'y': 4, 'z': 5}   '''

    __slots__ = ()
    def __new__(cls, orig={}, **kw):
        if kw:
            d = dict(orig, **kw)
            items = map(Item, d.items())
        else:
            try:
                items = map(Item, orig.items())
            except AttributeError:
                items = map(Item, orig)
        return frozenset.__new__(cls, items)

    def __repr__(self):
        cls = self.__class__.__name__
        items = frozenset.__iter__(self)
        _repr=", ".join(map(str,items))
        return '%s({%s})' % (cls, _repr)

    def __getitem__(self, key):
        if key not in self:
            raise KeyError(key)
        diff = self.difference
        item = diff(diff({key}))
        key, value = set(item).pop()
        return value

    def get(self, key, default=None):
        if key not in self:
            return default
        return self[key]

    def __iter__(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.key, items)

    def keys(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.key, items)

    def values(self):
        items = frozenset.__iter__(self)
        return map(lambda i: i.value, items)

    def items(self):
        items = frozenset.__iter__(self)
        return map(tuple, items)

    def copy(self):
        cls = self.__class__
        items = frozenset.copy(self)
        dupl = frozenset.__new__(cls, items)
        return dupl

    @classmethod
    def fromkeys(cls, keys, value):
        d = dict.fromkeys(keys,value)
        return cls(d)

    def __hash__(self):
        kv = tuple.__hash__
        items = frozenset.__iter__(self)
        return hash(frozenset(map(kv, items)))

    def __eq__(self, other):
        if not isinstance(other, FrozenDict):
            try:
                other = FrozenDict(other)
            except Exception:
                return False
        return frozenset.__eq__(self, other)

    def __ne__(self, other):
        return not self.__eq__(other)


if version == 2:
    #Here are the Python2 modifications
    class Python2(FrozenDict):
        def __iter__(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.key

        def iterkeys(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.key

        def itervalues(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield i.value

        def iteritems(self):
            items = frozenset.__iter__(self)
            for i in items:
                yield (i.key, i.value)

        def has_key(self, key):
            return key in self

        def viewkeys(self):
            return dict(self).viewkeys()

        def viewvalues(self):
            return dict(self).viewvalues()

        def viewitems(self):
            return dict(self).viewitems()

    #If this is Python2, rebuild the class
    #from scratch rather than use a subclass
    py3 = FrozenDict.__dict__
    py3 = {k: py3[k] for k in py3}
    py2 = {}
    py2.update(py3)
    dct = Python2.__dict__
    py2.update({k: dct[k] for k in dct})

    FrozenDict = type('FrozenDict', (frozenset,), py2)

  • Jedes Mal, wenn ich einen Kommentar wie diesen sehe, bin ich mir sicher, dass ich irgendwo einen Fehler gemacht und {} als Standard gesetzt habe, und gehe zurück und schaue mir meinen kürzlich geschriebenen Code an.

    – Ryan Hiebert

    27. Dezember 2011 um 23:30 Uhr

  • Ja, es ist ein böser Fallstrick, auf den jeder früher oder später stößt.

    – Markus Visser

    5. Januar 12 um 20:59 Uhr

  • Einfachere Formulierung: optional_dict_parm = optional_dict_parm or {}

    – Emmanuel

    13. Juni 12 um 12:08 Uhr

  • In diesem Fall können Sie verwenden types.MappingProxyType({}) als Standardwert für Argument.

    – IngwerPlusPlus

    10. April 15 um 19:50 Uhr

  • @Emmanuel Du willst das is None prüfen, um falsche Argumente abzufangen, wie zum Beispiel MappingProxyType({})oder wenn jemand einen Tippfehler gemacht hat, 0 zum Beispiel.

    – wjandrea

    2. November 21 um 20:38 Uhr

1643905570 549 Was ware ein eingefrorenes Diktat
Andrej Kortschak

Installieren eingefroren

pip install frozendict

Benutze es!

from frozendict import frozendict

def smth(param = frozendict({})):
    pass

  • Das ist auch schön, weil es hashbar ist und man von frozendict als Basisklasse erben kann. Beides ist nicht möglich, wenn Sie MappingProxyType verwenden.

    – Raumfahrer

    23. August 21 um 20:37 Uhr


.

757480cookie-checkWas wäre ein “eingefrorenes Diktat”?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy