Albero Attributi
Il problema con il vostro prima specifica è che Python non può dire in __getitem__
se, a my_obj.a.b.c.d
, vi verrà ora procederà più in basso un albero inesistente, nel qual caso ha bisogno di tornare un oggetto con un metodo __getitem__
in modo da non ottenere un valore AttributeError
o se si desidera un valore, nel qual caso è necessario restituire None
.
Io direi che in ogni caso hai sopra, dovresti aspettarti che lanci un KeyError
invece di restituire None
. Il motivo è che non si può sapere se None
significa "nessuna chiave" o "qualcuno ha effettivamente memorizzato None
in quella posizione". Per questo comportamento, tutto quello che dovete fare è prendere dotdictify
, rimuovere , e sostituirlo con __getitem__
:
def __getitem__(self, key):
return self[key]
Perché ciò che si vuole veramente è un dict
con __getattr__
e __setattr__
.
Ci può essere un modo per rimuovere __getitem__
del tutto e dire qualcosa di simile __getattr__ = dict.__getitem__
, ma penso che questo può essere eccesso di ottimizzazione, e sarà un problema se in seguito si decide che si desidera __getitem__
creare l'albero come va come dotdictify
originariamente fa, nel qual caso si cambiarlo in:
def __getitem__(self, key):
if key not in self:
dict.__setitem__(self, key, dotdictify())
return dict.__getitem__(self, key)
non mi piace il affari in originale dotdictify
.
percorso di supporto
La seconda specifica (override di get()
e set()
) è che un normale dict
ha un get()
che opera in modo diverso da ciò che si descrive e non ha nemmeno una set
(anche se ha una setdefault()
che è un'operazione inversa a get()
). Le persone si aspettano che lo get
prenda due parametri, il secondo è un valore predefinito se la chiave non viene trovata.
Se si desidera estendere __getitem__
e __setitem__
per gestire notazione chiave, è necessario modificare doctictify
a:
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__
Codice di prova:
>>> 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}}
Grazie mille, Mike. Ho aggiunto una funzione get che accetta la notazione dei punti (e un valore predefinito, come hai notato) Penso che questa nuova classe dotdictify renderà la vita molto più facile da gestire con dicts profondamente annidati. Grazie mille. – Hal
Hai bisogno di una funzione 'get()'? Che cosa fa l'esistente 'get()' no? Il 'get()' che hai descritto nella domanda è equivalente a 'get (key, None)' che ottieni gratuitamente da 'dict'. –
Quando ho usato la classe dotdictify "as-is" con l'installazione di Python 2.5 (Google App Engine SDK) la funzione get non stava gestendo le richieste di notazione dei punti per qualche motivo. Così ho scritto un wrapper veloce per la funzione get() per controllare la notazione dei punti e, in tal caso, passare a __getattr__ (restituendo il valore predefinito sull'eccezione), altrimenti passare a dict.get (self, key, default) – Hal