Attribut Baum
das Problem bei dem ersten Spezifikation ist, dass Python nicht in __getitem__
sagen kann, wenn bei my_obj.a.b.c.d
, werden Sie als nächstes weiter unten einen nicht vorhandenen Baum gehen, wobei in diesem Fall muss er zurück ein Objekt mit einer __getitem__
Methode, so dass Sie keine AttributeError
auf Sie geworfen werden, oder wenn Sie einen Wert möchten, in diesem Fall muss None
zurückgegeben werden.
Ich würde argumentieren, dass in jedem Fall Sie oben haben, sollten Sie erwarten, dass es eine KeyError
anstelle None
zurückwerfen wird. Der Grund dafür ist, dass Sie nicht wissen können, ob None
"kein Schlüssel" bedeutet oder "jemand tatsächlich None
an diesem Ort gespeichert hat". Für dieses Verhalten, alles, was Sie tun müssen, ist dotdictify
nehmen, entfernen Sie und ersetzen __getitem__
mit:
def __getitem__(self, key):
return self[key]
Weil das, was Sie wirklich wollen, eine dict
mit __getattr__
und __setattr__
ist.
Es kann ein Weg sein, __getitem__
vollständig zu entfernen und etwas zu sagen wie __getattr__ = dict.__getitem__
, aber ich denke, das Überoptimierung sein kann, und wird ein Problem sein, wenn Sie später Sie __getitem__
den Baum erstellen wollen entscheiden, wie es wie dotdictify
geht ursprünglich der Fall ist, in dem Fall, dass Sie es ändern würde:
def __getitem__(self, key):
if key not in self:
dict.__setitem__(self, key, dotdictify())
return dict.__getitem__(self, key)
ich mag nicht in der ursprünglichen dotdictify
die Geschäft.
Pfad Unterstützung
Die zweite Spezifikation (Überschreibung get()
und set()
) ist, dass ein normaler dict
eine get()
hat, die anders funktioniert, was Sie beschreiben und haben nicht einmal ein set
(obwohl es eine setdefault()
hat, die eine inverse Operation zu get()
). Die Benutzer erwarten, dass get
zwei Parameter verwendet, der zweite ist ein Standardwert, wenn der Schlüssel nicht gefunden wird.
Wenn Sie __getitem__
und __setitem__
erweitern möchten gepunkteten schlüssel Notation zu handhaben, müssen Sie doctictify
ändern:
class dotdictify(dict):
def __init__(self, value=None):
if value is None:
pass
elif isinstance(value, dict):
for key in value:
self.__setitem__(key, value[key])
else:
raise TypeError, 'expected dict'
def __setitem__(self, key, value):
if '.' in key:
myKey, restOfKey = key.split('.', 1)
target = self.setdefault(myKey, dotdictify())
if not isinstance(target, dotdictify):
raise KeyError, 'cannot set "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
target[restOfKey] = value
else:
if isinstance(value, dict) and not isinstance(value, dotdictify):
value = dotdictify(value)
dict.__setitem__(self, key, value)
def __getitem__(self, key):
if '.' not in key:
return dict.__getitem__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not isinstance(target, dotdictify):
raise KeyError, 'cannot get "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
return target[restOfKey]
def __contains__(self, key):
if '.' not in key:
return dict.__contains__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not isinstance(target, dotdictify):
return False
return restOfKey in target
def setdefault(self, key, default):
if key not in self:
self[key] = default
return self[key]
__setattr__ = __setitem__
__getattr__ = __getitem__
Prüfregeln:
>>> life = dotdictify({'bigBang': {'stars': {'planets': {}}}})
>>> life.bigBang.stars.planets
{}
>>> life.bigBang.stars.planets.earth = { 'singleCellLife' : {} }
>>> life.bigBang.stars.planets
{'earth': {'singleCellLife': {}}}
>>> life['bigBang.stars.planets.mars.landers.vikings'] = 2
>>> life.bigBang.stars.planets.mars.landers.vikings
2
>>> 'landers.vikings' in life.bigBang.stars.planets.mars
True
>>> life.get('bigBang.stars.planets.mars.landers.spirit', True)
True
>>> life.setdefault('bigBang.stars.planets.mars.landers.opportunity', True)
True
>>> 'landers.opportunity' in life.bigBang.stars.planets.mars
True
>>> life.bigBang.stars.planets.mars
{'landers': {'opportunity': True, 'vikings': 2}}
Vielen Dank, Mike. Ich habe eine get-Funktion hinzugefügt, die Punktnotation akzeptiert (und einen Standardwert, wie Sie notiert haben). Ich denke, dass diese neue dotdictify-Klasse das Leben mit tief verschachtelten Diktaten wesentlich erleichtern wird. Vielen Dank. – Hal
Brauchen Sie eine 'get()' Funktion? Was macht das, dass das vorhandene 'get()' nicht funktioniert? Das 'get()', das Sie in der Frage beschrieben haben, entspricht dem 'get (key, None)', das Sie kostenlos von 'dict' erhalten. –
Als ich die dotdictify-Klasse "wie besehen" mit meiner Python 2.5-Installation (Google App Engine SDK) verwendete, behandelte die get-Funktion aus irgendeinem Grund die Punktnotationsanforderungen nicht. Also schrieb ich einen schnellen Wrapper für die get() - Funktion, um nach Punktnotation zu suchen, und falls ja, pass auf __getattr__ (gibt die Standard-Ausnahme zurück), ansonsten gehe zu dict.get (self, key, default) – Hal