¿Cómo usar un punto “.” Para acceder a los miembros del diccionario?

¿Cómo puedo hacer que los miembros del diccionario de Python sean accesibles a través de un punto “.”?

Por ejemplo, en lugar de escribir mydict['val'] , me gustaría escribir mydict.val .

También me gustaría acceder a los dicts nesteds de esta manera. Por ejemplo

 mydict.mydict2.val 

se referiría a

 mydict = { 'mydict2': { 'val': ... } } 

    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 poder 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'] 

    Siempre he guardado esto en un archivo util. Puedes usarlo como una mezcla 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' 

    Instalar dotmap través de pip

     pip install dotmap 

    Hace todo lo que desea que haga y las 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, puede convertirlo en y desde objetos dict :

     d = m.toDict() m = DotMap(d) # automatic conversion in constructor 

    Esto significa que si algo a lo que desea acceder ya está en formato dict , puede convertirlo en un DotMap para un fácil acceso:

     import json jsonDict = json.loads(text) data = DotMap(jsonDict) print data.location.city 

    Finalmente, crea automáticamente nuevas instancias de DotMap secundarias para que pueda hacer cosas como esta:

     m = DotMap() m.people.steve.age = 31 

    Comparación a Bunch

    Revelación completa: soy el creador del DotMap . Lo creé porque a Bunch le faltaban estas características.

    • recordando los elementos del pedido se agregan e iterando en ese orden
    • Creación automática de DotMap infantil, que ahorra tiempo y DotMap un código más limpio cuando se tiene mucha jerarquía
    • construyendo a partir de un dict y convirtiendo recursivamente todas las instancias de dict niños a DotMap

    Derivar de dict y e implementar __getattr__ y __setattr__ .

    O puedes usar Bunch que es muy similar.

    No creo que sea posible hacer un Monkeypatch incorporado en la clase dict.

    Intenté esto:

     class dotdict(dict): def __getattr__(self, name): return self[name] 

    Puedes probar __getattribute__ también.

    Convierta cada dictado en un tipo de punto puntual sería suficiente, si desea iniciar esto desde un dict de múltiples capas, intente implementar __init__ también.

    La tela tiene una implementación muy agradable, mínima. Extendiendo eso para permitir el acceso nested, podemos usar un punto defaultdict , y el resultado es algo como esto:

     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 

    Utilízalo de la siguiente manera:

     keys = AttributeDict() keys.abc.xyz.x = 123 keys.abc.xyz.abc = 234 

    Eso explica un poco la respuesta de Kugel de “Derive from dict e e implementa __getattr__ and __setattr__ “. ¡Ya sabes cómo!

    Si desea seleccionar su diccionario modificado, necesita 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 

    No lo hagas El acceso a los atributos y la indexación son cosas separadas en Python, y no debes querer que realicen lo mismo. Cree una clase (posiblemente una creada por namedtuple ) si tiene algo que debería tener atributos accesibles y use la notación [] para obtener un elemento de un dict.

    Sobre la base de la respuesta de Kugel y teniendo en cuenta las palabras de precaución de Mike Graham, ¿qué sucede si hacemos una envoltura?

     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.ab += 2 >>>dw.ab += 2 >>>dw.a['c'] += 'Hello' >>>dw.a['c'] += ' World' >>>dw.ad >>>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 

    El lenguaje en sí no lo admite, pero a veces esto sigue siendo un requisito útil. Además de la receta Bunch, también puede escribir un pequeño método que puede acceder a un diccionario utilizando una cadena de puntos:

     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' >> 

    Me gusta el Munch y ofrece muchas opciones útiles además del acceso por puntos.

    importación de munch

    temp_1 = {‘persona’: {‘fname’: ‘senthil’, ‘lname’: ‘twiglingam’}}

    dict_munch = munch.munchify (temp_1)

    dict_munch.person.fname

     def dict_to_object(dick): # http://stackoverflow.com/a/1305663/968442 class Struct: def __init__(self, **entries): self.__dict__.update(entries) return Struct(**dick) 

    Si uno decide convertir permanentemente ese dict a objeto, debería hacerlo. Puede crear un objeto desechable justo antes de acceder.

     d = dict_to_object(d) 

    Para basarse en la respuesta de epool, esta versión le permite acceder a cualquier dictado dentro a través del operador de puntos:

     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] 

    Use __getattr__ , muy simple, funciona en Python 3.4.3

     class myDict(dict): def __getattr__(self,val): return self[val] blockBody=myDict() blockBody['item1']=10000 blockBody['item2']="StackOverflow" print(blockBody.item1) print(blockBody.item2) 

    Salida:

     10000 StackOverflow 

    Terminé probando AMBOS las bibliotecas AttrDict y Bunch y encontré que eran demasiado lentas para mis usos. Después de que un amigo y yo lo investigamos, encontramos que el método principal para escribir estas bibliotecas resulta en que la biblioteca se repita de forma agresiva a través de un objeto nested y haga copias del objeto del diccionario en todo momento. Con esto en mente, hicimos dos cambios clave. 1) Hicimos los atributos con carga lenta 2) En lugar de crear copias de un objeto de diccionario, creamos copias de un objeto de proxy ligero. Esta es la implementación final. El aumento de rendimiento de usar este código es increíble. Cuando usé AttrDict o Bunch, estas dos bibliotecas solo consumieron 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.5 ms). Por supuesto, esto depende de sus necesidades, pero si está utilizando esta funcionalidad en su código, definitivamente use algo tan 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 

    Vea la implementación original aquí en https://stackoverflow.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. Deberá escribirlos según sea necesario en los objetos DictProxy o ListProxy.

    Me gustaría lanzar mi propia solución al ring:

    https://github.com/skorokithakis/jsane

    Te permite analizar JSON en algo a lo que puedes acceder with.attribute.lookups.like.this.r() , principalmente porque no había visto esta respuesta antes de comenzar a trabajar en ello.

    No es una respuesta directa a la pregunta del OP, pero está inspirada y quizás sea útil para algunos … He creado una solución basada en objetos utilizando el __dict__ interno (el código de ninguna manera 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 eg 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" 

    Una forma sencilla de obtener acceso de punto (pero no acceso de matriz), es usar un objeto plano en Python. Me gusta esto:

     class YourObject: def __init__(self, *args, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) 

    … y utilízalo así:

     >>> obj = YourObject(key="value") >>> print(obj.key) "value" 

    … para convertirlo en un dict:

     >>> print(obj.__dict__) {"key": "value"} 

    Esta solución es un refinamiento sobre el ofrecido por epool para abordar el requisito del OP para acceder a los dictados nesteds de manera consistente. La solución de epool no permitía acceder a los dicts nesteds.

     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 puede ahora hacer algo como: ABCD .

    Una solución algo delicada.

     class DotDict(dict): __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ def __getattr__(self, key): def typer(candidate): if isinstance(candidate, dict): return DotDict(candidate) if isinstance(candidate, str): # iterable but no need to iter return candidate try: # other iterable are processed as list return [typer(item) for item in candidate] except TypeError: return candidate return candidate return typer(dict.get(self, key))