Gibt es eine Möglichkeit, Unterklassen von dict und collection.abc.MutableMapping zusammen zu klassifizieren?

26

Nehmen wir als Beispiel an, ich möchte eine Unterklasse erstellen dictund alle Schlüssel großschreiben lassen:

class capdict(dict):
    def __init__(self,*args,**kwds):
        super().__init__(*args,**kwds)
        mod = [(k.capitalize(),v) for k,v in super().items()]
        super().clear()
        super().update(mod)
    def __getitem__(self,key):
        return super().__getitem__(key.capitalize())
    def __setitem__(self,key,value):
        super().__setitem__(key.capitalize(),value)
    def __delitem__(self,key):
        super().__detitem__(key.capitalize())

Dies funktioniert bis zu einem gewissen Grad,

>>> ex = capdict(map(reversed,enumerate("abc")))
>>> ex
{'A': 0, 'B': 1, 'C': 2}
>>> ex['a']
0

aber natürlich nur für Methoden, an deren Implementierung ich mich zum Beispiel erinnerte

>>> 'a' in ex
False

ist nicht das gewünschte Verhalten.

Die faule Art, alle Methoden auszufüllen, die aus den "Kern" -Methoden abgeleitet werden können, wäre nun das Einmischen collections.abc.MutableMapping. Nur funktioniert es hier nicht. Ich nehme an, weil die fraglichen Methoden ( __contains__im Beispiel) bereits von bereitgestellt werden dict.

Gibt es eine Möglichkeit, meinen Kuchen zu haben und ihn zu essen? Etwas Magie, um MutableMappingnur die Methoden zu sehen, die ich überschrieben habe, damit die anderen basierend auf diesen neu implementiert werden?

Paul Panzer
quelle
Möglicherweise müssen Sie nicht verwenden MutableMapping. Siehe Wörterbuch ohne Berücksichtigung der Groß- und Kleinschreibung .
Martineau
@martineau danke, wie gesagt es war nur ein Beispiel.
Paul Panzer
Sie könnten verwenden os._Environ.
Peter Wood

Antworten:

26

Was Sie tun könnten:

Dies wird wahrscheinlich nicht gut funktionieren (dh nicht das sauberste Design), aber Sie könnten zuerst von MutableMapping und dann von dict second erben .

Dann würde MutableMapping alle von Ihnen implementierten Methoden verwenden (da diese die ersten in der Suchkette sind):

>>> class D(MutableMapping, dict):
        def __getitem__(self, key):
            print(f'Intercepted a lookup for {key!r}')
            return dict.__getitem__(self, key)


>>> d = D(x=10, y=20)
>>> d.get('x', 0)
Intercepted a lookup for 'x'
10
>>> d.get('z', 0)
Intercepted a lookup for 'z'
0

Besserer Weg:

Der sauberste Ansatz (leicht zu verstehen und zu testen) besteht darin, nur von MutableMapping zu erben und dann die erforderlichen Methoden unter Verwendung eines regulären Diktats als Basisdatenspeicher zu implementieren (mit Zusammensetzung statt Vererbung):

>>> class CapitalizingDict(MutableMapping):
        def __init__(self, *args, **kwds):
            self.store = {}
            self.update(*args, **kwds)
        def __getitem__(self, key):
            key = key.capitalize()
            return self.store[key]
        def __setitem__(self, key, value):
            key = key.capitalize()
            self.store[key] = value
        def __delitem__(self, key):
            del self.store[key]
        def __len__(self):
            return len(self.store)
        def __iter__(self):
            return iter(self.store)
        def __repr__(self):
            return repr(self.store)


>>> d = CapitalizingDict(x=10, y=20)
>>> d
{'X': 10, 'Y': 20}
>>> d['x']
10
>>> d.get('x', 0)
10
>>> d.get('z', 0)
0
>>> d['w'] = 30
>>> d['W']
30
Raymond Hettinger
quelle
Vielen Dank! Ich hätte schwören können, dass ich beide Befehle ausprobiert habe ... Aus Interesse, wenn ich die "könnte" -Methode verwende, tausche ich alle supers gegen explizite dicts aus, dann scheint es zu funktionieren, außer lenRückgaben 0. Wo kommt das her?
Paul Panzer
2
Der super () Aufruf von __len _ () __ geht zum nächsten im mro : (D, MutableMapping, dict). Dies ist die MutableMappiing .__ len __ () -Methode, die immer 0 zurückgibt. Sie sollte nicht direkt aufgerufen werden - sie sollte immer überschrieben werden. Deshalb müssen Sie dict.__len__(self)direkt anrufen . Und das ist einer der Gründe, warum ich sagte "das wird wahrscheinlich nicht gut funktionieren" ;-)
Raymond Hettinger