python - create - Acceso recursivo dict a través de los atributos, así como el acceso al índice?
dict django (2)
Me gustaría poder hacer algo como esto:
from dotDict import dotdictify
life = {''bigBang'':
{''stars'':
{''planets'': []}
}
}
dotdictify(life)
# This would be the regular way:
life[''bigBang''][''stars''][''planets''] = {''earth'': {''singleCellLife'': {}}}
# But how can we make this work?
life.bigBang.stars.planets.earth = {''singleCellLife'': {}}
#Also creating new child objects if none exist, using the following syntax:
life.bigBang.stars.planets.earth.multiCellLife = {''reptiles'':{},''mammals'':{}}
Mis motivaciones son mejorar la brevedad del código y, si es posible, usar una sintaxis similar a Javascript para acceder a los objetos JSON para un desarrollo cruzado eficiente. (También uso Py2JS y similares).
Debajo de otra implementación de un diccionario de atributos anidados (inspirado en la respuesta de Curt Hagenlocher, desglosado en lo esencial):
class AttrDict(dict):
""" Nested Attribute Dictionary
A class to convert a nested Dictionary into an object with key-values
accessibly using attribute notation (AttrDict.attribute) in addition to
key notation (Dict["key"]). This class recursively sets Dicts to objects,
allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
"""
def __init__(self, mapping):
super(AttrDict, self).__init__()
for key, value in mapping.items():
self.__setitem__(key, value)
def __setitem__(self, key, value):
if isinstance(value, dict):
value = AttrDict(value)
super(AttrDict, self).__setitem__(key, value)
def __getattr__(self, item):
try:
return self.__getitem__(item)
except KeyError:
raise AttributeError(item)
__setattr__ = __setitem__
Esto funciona tanto en Python 2 como en 3:
life = AttrDict({''bigBang'': {''stars'': {''planets'': {}}}})
life[''bigBang''][''stars''][''planets''] = {''earth'': {''singleCellLife'': {}}}
life.bigBang.stars.planets.earth.multiCellLife = {''reptiles'': {}, ''mammals'': {}}
print(life.bigBang.stars.planets.earth)
# -> {''singleCellLife'': {}, ''multiCellLife'': {''mammals'': {}, ''reptiles'': {}}}
La conversión de KeyError en AttributeError en __getattr__
es necesaria en Python3 de modo que hasattr
funcione también en caso de que no se encuentre el atributo:
hasattr(life, ''parallelUniverse'')
# --> False
Esta es una forma de crear ese tipo de experiencia:
class DotDictify(dict):
MARKER = object()
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 isinstance(value, dict) and not isinstance(value, DotDictify):
value = DotDictify(value)
super(DotDictify, self).__setitem__(key, value)
def __getitem__(self, key):
found = self.get(key, DotDictify.MARKER)
if found is DotDictify.MARKER:
found = DotDictify()
super(DotDictify, self).__setitem__(key, found)
return found
__setattr__, __getattr__ = __setitem__, __getitem__
if __name__ == ''__main__'':
life = {''bigBang'':
{''stars'':
{''planets'': {} # Value changed from []
}
}
}
life = DotDictify(life)
print(life.bigBang.stars.planets) # -> []
life.bigBang.stars.planets.earth = {''singleCellLife'' : {}}
print(life.bigBang.stars.planets) # -> {''earth'': {''singleCellLife'': {}}}