python - keys - ¿Cómo usar un punto "." Para acceder a los miembros del diccionario?
python empty dictionary (18)
¿Cómo puedo acceder a los miembros del diccionario de Python mediante un punto "."?
Por ejemplo, en lugar de escribir mydict[''val'']
, me gustaría escribir mydict.val
.
También me gustaría acceder a los dictados anidados de esta manera. Por ejemplo
mydict.mydict2.val
se referiría a
mydict = { ''mydict2'': { ''val'': ... } }
Derive de dict e implemente __getattr__
y __setattr__
.
O puedes usar Bunch que es muy similar.
No creo que sea posible hacer una clase de Dict incorporada en el monopatín.
El lenguaje en sí no es compatible con esto, pero a veces esto sigue siendo un requisito útil. Además de la receta de Bunch, también puedes escribir un pequeño método que puede acceder a un diccionario usando una cadena punteada:
def get_var(input_dict, accessor_string):
"""Gets data from a dictionary using a dotted accessor-string"""
current_data = input_dict
for chunk in accessor_string.split(''.''):
current_data = current_data.get(chunk, {})
return current_data
que apoyaría algo como esto:
>> test_dict = {''thing'': {''spam'': 12, ''foo'': {''cheeze'': ''bar''}}}
>> output = get_var(test_dict, ''thing.spam.foo.cheeze'')
>> print output
''bar''
>>
Esta solución es un refinamiento de la ofrecida por epool para abordar el requisito de OP para acceder a los dictados anidados de manera consistente. La solución de epool no permitió el acceso a los dictados anidados.
class YAMLobj(dict):
def __init__(self, args):
super(YAMLobj, self).__init__(args)
if isinstance(args, dict):
for k, v in args.iteritems():
if not isinstance(v, dict):
self[k] = v
else:
self.__setattr__(k, YAMLobj(v))
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(YAMLobj, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(YAMLobj, self).__delitem__(key)
del self.__dict__[key]
Con esta clase, uno ahora puede hacer algo como: ABCD
.
Instalar dotmap
través de pip
pip install dotmap
Hace todo lo que quiere hacer y subclases dict
, por lo que funciona como un diccionario normal:
from dotmap import DotMap
m = DotMap()
m.hello = ''world''
m.hello
m.hello += ''!''
# m.hello and m[''hello''] now both return ''world!''
m.val = 5
m.val2 = ''Sam''
Además de eso, puedes convertirlo hacia y desde objetos dict
:
d = m.toDict()
m = DotMap(d) # automatic conversion in constructor
Esto significa que si algo que desea acceder ya está en formato dict
, puede convertirlo en un DotMap
para facilitar el acceso:
import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city
Finalmente, crea automáticamente nuevas instancias de DotMap
para que pueda hacer cosas como esta:
m = DotMap()
m.people.steve.age = 31
Comparación con Bunch
DotMap completa: soy el creador de DotMap . Lo creé porque a Bunch
le faltaban estas características
- recordando los elementos de orden se agregan e iteran en ese orden
- Creación automática de
DotMap
niños, que ahorra tiempo yDotMap
un código más limpio cuando tiene mucha jerarquía - construir desde un
dict
y convertir recursivamente todas las instanciasdict
secundarias aDotMap
Intenté esto:
class dotdict(dict):
def __getattr__(self, name):
return self[name]
puedes probar __getattribute__
también.
hacer que cada dicción sea un tipo de dotdict sería lo suficientemente bueno, si quieres iniciar esto desde un dict de capas múltiples, intenta implementar __init__
también.
Me gusta Munch y ofrece muchas opciones útiles además del acceso a puntos.
importación masca
temp_1 = {''persona'': {''fname'': ''senthil'', ''lname'': ''ramalingam''}}
dict_munch = munch.munchify (temp_1)
dict_munch.person.fname
Me gustaría lanzar mi propia solución al ring:
https://github.com/skorokithakis/jsane
Le permite analizar JSON en algo a lo que puede acceder with.attribute.lookups.like.this.r()
, sobre todo porque no había visto esta respuesta antes de empezar a trabajar en ella.
No es una respuesta directa a la pregunta del OP, sino inspirada y quizás útil para algunos. He creado una solución basada en objetos usando el __dict__
interno (de ningún modo código optimizado)
payload = {
"name": "John",
"location": {
"lat": 53.12312312,
"long": 43.21345112
},
"numbers": [
{
"role": "home",
"number": "070-12345678"
},
{
"role": "office",
"number": "070-12345679"
}
]
}
class Map(object):
"""
Dot style access to object members, access raw values
with an underscore e.g.
class Foo(Map):
def foo(self):
return self.get(''foo'') + ''bar''
obj = Foo(**{''foo'': ''foo''})
obj.foo => ''foobar''
obj._foo => ''foo''
"""
def __init__(self, *args, **kwargs):
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self.__dict__[k] = v
self.__dict__[''_'' + k] = v
if kwargs:
for k, v in kwargs.iteritems():
self.__dict__[k] = v
self.__dict__[''_'' + k] = v
def __getattribute__(self, attr):
if hasattr(self, ''get_'' + attr):
return object.__getattribute__(self, ''get_'' + attr)()
else:
return object.__getattribute__(self, attr)
def get(self, key):
try:
return self.__dict__.get(''get_'' + key)()
except (AttributeError, TypeError):
return self.__dict__.get(key)
def __repr__(self):
return u"<{name} object>".format(
name=self.__class__.__name__
)
class Number(Map):
def get_role(self):
return self.get(''role'')
def get_number(self):
return self.get(''number'')
class Location(Map):
def get_latitude(self):
return self.get(''lat'') + 1
def get_longitude(self):
return self.get(''long'') + 1
class Item(Map):
def get_name(self):
return self.get(''name'') + " Doe"
def get_location(self):
return Location(**self.get(''location''))
def get_numbers(self):
return [Number(**n) for n in self.get(''numbers'')]
# Tests
obj = Item({''foo'': ''bar''}, **payload)
assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112
for n in obj.numbers:
assert type(n) == Number
if n.role == ''home'':
assert n.number == "070-12345678"
if n.role == ''office'':
assert n.number == "070-12345679"
No lo hagas El acceso de atributos y la indexación son cosas separadas en Python, y no debería querer que realicen lo mismo. Haga una clase (posiblemente una creada por namedtuple
) si tiene algo que debería tener atributos accesibles y use notación []
para obtener un elemento de un dict.
Para construir sobre la respuesta de epool, esta versión le permite acceder a cualquier dictado dentro del operador de punto:
foo = {
"bar" : {
"baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
}
}
Por ejemplo, foo.bar.baz[1].baba
devuelve "loo"
.
class Map(dict):
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
if isinstance(v, dict):
v = Map(v)
if isinstance(v, list):
self.__convert(v)
self[k] = v
if kwargs:
for k, v in kwargs.iteritems():
if isinstance(v, dict):
v = Map(v)
elif isinstance(v, list):
self.__convert(v)
self[k] = v
def __convert(self, v):
for elem in xrange(0, len(v)):
if isinstance(v[elem], dict):
v[elem] = Map(v[elem])
elif isinstance(v[elem], list):
self.__convert(v[elem])
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
Puedes hacerlo usando esta clase que acabo de hacer. Con esta clase puede usar el objeto Map
como otro diccionario (incluida la serialización json) o con la notación de puntos. Espero ayudarte:
class Map(dict):
"""
Example:
m = Map({''first_name'': ''Eduardo''}, last_name=''Pool'', age=24, sports=[''Soccer''])
"""
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self[k] = v
if kwargs:
for k, v in kwargs.iteritems():
self[k] = v
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
Ejemplos de uso:
m = Map({''first_name'': ''Eduardo''}, last_name=''Pool'', age=24, sports=[''Soccer''])
# Add new key
m.new_key = ''Hello world!''
# Or
m[''new_key''] = ''Hello world!''
print m.new_key
print m[''new_key'']
# Update values
m.new_key = ''Yay!''
# Or
m[''new_key''] = ''Yay!''
# Delete key
del m.new_key
# Or
del m[''new_key'']
Si desea recuperar su diccionario modificado, debe agregar algunos métodos de estado a las respuestas anteriores:
class DotDict(dict):
"""dot.notation access to dictionary attributes"""
def __getattr__(self, attr):
return self.get(attr)
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
def __getstate__(self):
return self
def __setstate__(self, state):
self.update(state)
self.__dict__ = self
Siempre he guardado esto en un archivo util. Puedes usarlo como mixin en tus propias clases también.
>>> class dotdict(dict):
... """dot.notation access to dictionary attributes"""
... __getattr__ = dict.get
... __setattr__ = dict.__setitem__
... __delattr__ = dict.__delitem__
...
>>> mydict = {''val'':''it works''}
>>> nested_dict = {''val'':''nested works too''}
>>> mydict = dotdict(mydict)
>>> mydict.val
''it works''
>>> mydict.nested = dotdict(nested_dict)
>>> mydict.nested.val
''nested works too''
Sobre la base de la respuesta de Kugel y teniendo en cuenta las palabras de precaución de Mike Graham, ¿qué pasaría si hacemos un envoltorio?
class DictWrap(object):
""" Wrap an existing dict, or create a new one, and access with either dot
notation or key lookup.
The attribute _data is reserved and stores the underlying dictionary.
When using the += operator with create=True, the empty nested dict is
replaced with the operand, effectively creating a default dictionary
of mixed types.
args:
d({}): Existing dict to wrap, an empty dict is created by default
create(True): Create an empty, nested dict instead of raising a KeyError
example:
>>>dw = DictWrap({''pp'':3})
>>>dw.a.b += 2
>>>dw.a.b += 2
>>>dw.a[''c''] += ''Hello''
>>>dw.a[''c''] += '' World''
>>>dw.a.d
>>>print dw._data
{''a'': {''c'': ''Hello World'', ''b'': 4, ''d'': {}}, ''pp'': 3}
"""
def __init__(self, d=None, create=True):
if d is None:
d = {}
supr = super(DictWrap, self)
supr.__setattr__(''_data'', d)
supr.__setattr__(''__create'', create)
def __getattr__(self, name):
try:
value = self._data[name]
except KeyError:
if not super(DictWrap, self).__getattribute__(''__create''):
raise
value = {}
self._data[name] = value
if hasattr(value, ''items''):
create = super(DictWrap, self).__getattribute__(''__create'')
return DictWrap(value, create)
return value
def __setattr__(self, name, value):
self._data[name] = value
def __getitem__(self, key):
try:
value = self._data[key]
except KeyError:
if not super(DictWrap, self).__getattribute__(''__create''):
raise
value = {}
self._data[key] = value
if hasattr(value, ''items''):
create = super(DictWrap, self).__getattribute__(''__create'')
return DictWrap(value, create)
return value
def __setitem__(self, key, value):
self._data[key] = value
def __iadd__(self, other):
if self._data:
raise TypeError("A Nested dict will only be replaced if it''s empty")
else:
return other
Terminé probando AMBAS bibliotecas AttrDict y Bunch y descubrí que eran formas de ralentizar mi uso. Después de que un amigo y yo lo investigamos, descubrimos que el método principal para escribir estas bibliotecas hace que la biblioteca repita agresivamente a través de un objeto anidado y haga copias del objeto del diccionario en todo momento. Con esto en mente, hicimos dos cambios clave. 1) Hicimos atributos con carga diferida 2) en lugar de crear copias de un objeto de diccionario, creamos copias de un objeto proxy liviano. Esta es la implementación final. El aumento en el rendimiento de usar este código es increíble. Al usar AttrDict o Bunch, estas dos bibliotecas consumieron solo 1/2 y 1/3 respectivamente de mi tiempo de solicitud (¿¡qué !?). Este código redujo ese tiempo a casi nada (en algún lugar en el rango de 0.5ms). Esto, por supuesto, depende de tus necesidades, pero si estás usando esta funcionalidad bastante en tu código, definitivamente ve con algo simple como este.
class DictProxy(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return wrap(self.obj[key])
def __getattr__(self, key):
try:
return wrap(getattr(self.obj, key))
except AttributeError:
try:
return self[key]
except KeyError:
raise AttributeError(key)
# you probably also want to proxy important list properties along like
# items(), iteritems() and __len__
class ListProxy(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return wrap(self.obj[key])
# you probably also want to proxy important list properties along like
# __iter__ and __len__
def wrap(value):
if isinstance(value, dict):
return DictProxy(value)
if isinstance(value, (tuple, list)):
return ListProxy(value)
return value
Consulte la implementación original here en https://.com/users/704327/michael-merickel .
La otra cosa a tener en cuenta, es que esta implementación es bastante simple y no implementa todos los métodos que pueda necesitar. Tendrá que escribir los que sean necesarios en los objetos DictProxy o ListProxy.
Una forma simple de obtener acceso por punto (pero no por acceso de matriz) es usar un objeto simple en Python. Me gusta esto:
class YourObject:
def __init__(self, *args, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
... y usarlo así:
>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"
... para convertirlo en un dict:
>>> print(obj.__dict__)
{"key": "value"}
Fabric tiene una implementation realmente agradable y mínima. Ampliando eso para permitir el acceso anidado, podemos usar un defaultdict
, y el resultado se ve así:
from collections import defaultdict
class AttributeDict(defaultdict):
def __init__(self):
super(AttributeDict, self).__init__(AttributeDict)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
self[key] = value
Haz uso de él de la siguiente manera:
keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234
Eso se desarrolla un poco en la respuesta de Kugel de "Derive from dict and and implementation __getattr__
and __setattr__
". ¡Ahora sabes cómo!
def dict_to_object(dick):
# http://.com/a/1305663/968442
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
return Struct(**dick)
Si uno decide convertir permanentemente esa dict
en objeto, esto debería hacer. Puede crear un objeto desechable justo antes de acceder.
d = dict_to_object(d)