Cómo usar un punto "." para acceder a los miembros del diccionario?

283

¿Cómo hago que los miembros del diccionario 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 dictados anidados de esta manera. Por ejemplo

mydict.mydict2.val 

se referiría a

mydict = { 'mydict2': { 'val': ... } }
bodacydo
fuente
20
Muchas de las situaciones en las que las personas usan dictados anidados serían tan bien o mejor servidos por dictados con tuplas como claves, donde d[a][b][c]se reemplaza por d[a, b, c].
Mike Graham
77
No es magia: foo = {}; foo [1,2,3] = "uno, dos, tres!"; foo.keys () => [(1,2,3)]
Bryan Oakley
10
Guau. Wow otra vez No sabía que las tuplas podrían ser claves de dict. Guau tercera vez.
bodacydo
3
Cualquier objeto que sea "hashable" puede usarse como clave de un dict. La mayoría de los objetos inmutables también son hashable, pero solo si todo su contenido es hashaable. El código d [1, 2, 3] funciona porque "," es el "crear un operador de tupla"; es lo mismo que d [(1, 2, 3)]. Los paréntesis son a menudo opcionales alrededor de la declaración de una tupla.
Larry Hastings
66
¿Ha considerado el caso donde la clave tiene un punto por sí mismo {"my.key":"value"}? ¿O cuando la clave es una palabra clave, como "desde"? Lo he considerado un par de veces, y son más problemas y resolución de problemas que beneficios percibidos.
Todor Minakov

Respuestas:

147

Puedes hacerlo usando esta clase que acabo de hacer. Con esta clase, puede usar el Mapobjeto 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']
epool
fuente
21
Para trabajar en Python 3 He actualizado .iteritems()a.items()
berto
13
Tenga en cuenta que esto se comportará de manera diferente a las expectativas comunes, ya que no aumentará AttributeErrorsi el atributo no existe. En cambio, volverá None.
mic_e
Recomendamos agregar getstate y setstate para que la copia profunda y otros sistemas puedan soportarlo.
user1363990
44
Puede simplificar su constructor a self.update(*args,**kwargs). Además, puedes agregar __missing__(self,key): value=self[key]= type(self)(); return value. Luego puede agregar entradas faltantes utilizando la notación de puntos. Si desea que sea seleccionable, puede agregar __getstate__y__setstate__
Jens Munk
1
Esto haría hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
Xiao
266

Siempre he guardado esto en un archivo util. También puedes usarlo como mezcla en tus propias clases.

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'
derek73
fuente
55
Respuesta muy simple, genial! ¿Sabes lo que necesitaría para completar las pestañas en el trabajo de IPython? La clase necesitaría implementar __dir __ (self), pero de alguna manera no puedo hacer que funcione.
andreas-h
8
+1 por simplicidad. pero no parece funcionar en dictados anidados. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.bararroja un error de atributo, pero d.foofunciona bien.
tmthyjames
2
Sí, esto no funciona para estructuras anidadas complejas.
David
17
@tmthyjames, simplemente puede devolver el objeto de tipo dotdict en el método getter para acceder de forma recursiva a atributos con notación de puntos como: python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
TMKasun
44
Después de experimentar con él, parece getser una mala idea, ya que volverá en Nonelugar de generar un error por elementos faltantes ...
NichtJens
117

Instalar a dotmaptravés depip

pip install dotmap

Hace todo lo que quieres que haga 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, puede convertirlo hacia y desde dictobjetos:

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

Esto significa que si algo a lo que desea acceder ya está en dictforma, puede convertirlo en un DotMapacceso fácil:

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

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

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

Comparación con Bunch

Divulgación completa: soy el creador del DotMap . Lo creé porque Bunchfaltaban estas características

  • Recordando los artículos de pedido se agregan e iterando en ese orden
  • DotMapcreación secundaria automática , lo que ahorra tiempo y crea un código más limpio cuando tiene mucha jerarquía
  • construyendo desde dictay convirtiendo recursivamente todas las dictinstancias secundarias aDotMap
Chris Redford
fuente
2
:-) ¿puedes hacer que funcione con teclas que ya tienen un punto en el nombre? {"test.foo": "bar"}Se puede acceder a través de mymap.test.fooEso sería fantástico. Se necesitará cierta regresión para convertir un mapa plano en un mapa profundo y luego aplicarle DotMap, ¡pero vale la pena!
dlite922
Ordenado. ¿Alguna forma de hacer que el listado / finalización de pestañas funcione con las teclas del cuaderno Jupyter? El acceso al estilo de puntos es muy valioso para uso interactivo.
Dmitri
Producto @Dmitri Cool. Nunca he oído hablar de él antes, así que no estoy seguro de cómo hacer que su autocompletado funcione. Estoy de acuerdo en que usar DotMapcon autocompletar funciona mejor. Uso Sublime Text, que autocompleta palabras clave escritas previamente.
Chris Redford
1
Me parece que carece de extracción de diccionario para cosas como **kwargso c = {**a, **b}. De hecho, falla silenciosamente, se comporta como un diccionario vacío cuando se extrae.
Simon Streicher
@SimonStreicher Probé esto m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));y obtuve lo esperado 2 3. Si tiene un caso roto comprobado que funciona dict()pero no DotMap(), envíe su código a la pestaña Problemas en GitHub.
Chris Redford
56

Derivar de dict e implementar __getattr__e __setattr__.

O puede usar Bunch, que es muy similar.

No creo que sea posible monopatch incorporado en la clase dict.

Kugel
fuente
2
¿Qué significa monkeypatch exactamente? Lo he escuchado pero no lo he usado. (Perdón por hacer esas preguntas de novato, todavía no soy tan bueno con la programación (solo soy estudiante de segundo año))
bodacydo
9
Monkeypatching está utilizando la dinámica de Python (o cualquier idioma) para cambiar algo que generalmente se definiría en el código fuente. Se aplica especialmente a cambiar la definición de clases después de que se crean.
Mike Graham
Si usa mucho esta funcionalidad, tenga cuidado con la velocidad de Bunch. Lo estaba usando con bastante frecuencia y terminó consumiendo un tercio de mi tiempo de solicitud. Mira mi respuesta para una explicación más detallada de esto.
JayD3e
22

Fabric tiene una implementación realmente agradable y mínima . Extendiendo eso para permitir el acceso anidado, podemos usar a 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 la siguiente manera:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

Eso explica un poco la respuesta de Kugel de "Derivar de dict e implementar __getattr__e __setattr__". ¡Ahora ya sabes cómo!

Dave
fuente
1
¡Ese es genial!
Thomas Klinger
Es bueno incluir un defaultdict; sin embargo, esto parece funcionar solo cuando se inicia un dictado desde cero. Si necesitamos convertir un dict existente a un "dotdict" recursivamente. Aquí hay una alternativa dotdictque permite convertir dictobjetos existentes de forma recursiva: gist.github.com/miku/…
miku
19

Intenté esto:

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

Puedes intentarlo __getattribute__también.

hacer que cada dict sea un tipo de dotdict sería lo suficientemente bueno, si desea iniciar esto desde un dict multicapa, intente implementar __init__también.

tdihp
fuente
Uy, la respuesta de @ Kugel es similar.
tdihp
1
tdihp, todavía me gusta tu respuesta porque la entendí más rápido: tiene el código real.
Yigal
1
+1 para el código real. Pero la sugerencia de @ Kugel de usar Bunch también es muy buena.
Dannid
Me pareció útil insertar esto dentro de una función colocando def docdict(name):antes y luego `if isinstance (name, dict): return DotDict (name) return name`
Daniel Moskovich
gran ejemplo simple ... Extendí esto un poco para que un dict anidado se encadene fácilmente, similar a @DanielMoskovich, pero también devuelva nodos de hoja correctamente para int, string, etc ... o nulo si no se encuentraclass dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
D Sievers
11

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

Mike Graham
fuente
Gracias por la respuesta. Pero eche un vistazo a esta pregunta que también acabo de hacer: stackoverflow.com/questions/2352252/... Esto parece una buena idea para usar en .lugar de []acceder a estructuras de datos complicadas en las plantillas de Mako.
bodacydo
2
Puedo ver un caso de uso para esto; De hecho, lo hice hace solo un par de semanas. En mi caso, quería un objeto al que pudiera acceder a los atributos con notación de puntos. Encontré que es muy fácil simplemente heredar de dict, así que tengo todas las características de dict incorporadas, pero la interfaz pública para este objeto usa notación de puntos (es esencialmente una interfaz de solo lectura para algunos datos estáticos). Mis usuarios están mucho más contentos con 'foo.bar' que con 'foo ["bar"]' y estoy feliz de poder aprovechar las características del tipo de datos dict.
Bryan Oakley
10
Ya conoces un buen estilo de Python: te lo decimos, no finjas que los valores de un dict son atributos. Es una mala practica. Por ejemplo, ¿qué sucede si desea almacenar un valor con el mismo nombre que un atributo existente de un dict, como "elementos" o "get" o "pop"? Probablemente algo confuso. ¡Entonces no lo hagas!
Larry Hastings
55
Vaya, me olvidé de atributos como 'elementos', 'obtener' o 'pop. ¡Gracias por mencionar este importante ejemplo!
bodacydo
55
@ Gabe, ha pasado mucho tiempo ... pero creo que vale la pena decirlo. No es "lo suficientemente bueno en JS": es "lo suficientemente horrible en JS". Se vuelve divertido cuando almacena claves / atributos que tienen el mismo nombre que otros atributos importantes en la cadena prototípica.
bgusach
11

Si desea encurtir 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
volodymyr
fuente
Gracias por el comentario sobre el decapado. Este error me volvió loco y ¡solo me di cuenta de que era por este problema!
Shagru
También sucede cuando usa copy.deepcopy. Esta adición es necesaria.
user1363990
Simplificación:__getattr__ = dict.get
martineau
9

Sobre la base de la respuesta de Kugel y teniendo en cuenta las palabras de precaución de Mike Graham, ¿qué pasa 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
MJ
fuente
8

Uso SimpleNamespace:

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])
Dmitry Zotikov
fuente
1
Este enfoque funciona mejor. (con json cargado desde el archivo)
ged
¿Esto explica los dictados anidados?
Mojimi
1
No es compatible con Dict anidado. docs.python.org/3.3/library/types.html#types.SimpleNamespace
Carson
6

Me gusta el Munch y ofrece muchas opciones prácticas además del acceso de punto.

importación munch

temp_1 = {'persona': {'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname

Senthil
fuente
6

Recientemente me encontré con la biblioteca ' Box ' que hace lo mismo.

Comando de instalación: pip install python-box

Ejemplo:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

Descubrí que es más efectivo que otras bibliotecas existentes como dotmap, que generan un error de recursión de Python cuando tienes grandes dictados anidados.

Enlace a la biblioteca y detalles: https://pypi.org/project/python-box/

Pradip Gupta
fuente
5

Uso __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
IRSHAD
fuente
4

El lenguaje en sí no es compatible con esto, pero a veces 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 usando 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'
>>
pbanka
fuente
4

Para construir sobre la respuesta de epool, esta versión le permite acceder a cualquier dict a través del operador de punto:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

Por ejemplo, foo.bar.baz[1].babavuelve "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]
toca mi cuerpo
fuente
1
Python 3: reemplazar iteritems()con items()y xrange()conrange()
sasawatc
3
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 convertirlo permanentemente dicten un objeto, esto debería hacerlo. Puede crear un objeto desechable justo antes de acceder.

d = dict_to_object(d)
nehem
fuente
def attr (** kwargs): o = lambda: Ninguno o .__ dict __. update (** kwargs) return o
throws_exceptions_at_you
2

Terminé probando AMBOS el AttrDict y el Bunchbibliotecas y descubrí que son una forma lenta para mis usos. Después de que un amigo y yo lo investigamos, descubrimos que el método principal para escribir estas bibliotecas hace que la biblioteca recurra 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) Creamos atributos con carga lenta 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 de rendimiento del uso de este código es increíble. Al usar 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.5ms). Por supuesto, esto depende de sus necesidades, pero si está utilizando esta funcionalidad bastante en su código,

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

Ver la implementación original aquí por 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.

JayD3e
fuente
0

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(), principalmente porque no había visto esta respuesta antes de comenzar a trabajar en ella.

Stavros Korokithakis
fuente
Python es culpable de algunos errores molestos de diseño simple, el aumento KeyErrores uno de ellos. Cuando se accede a la clave que no existe, todo lo que tiene que hacer es regresar de manera Nonesimilar al comportamiento de JS. Soy un gran admirador de la autovivificación tanto para leer como para escribir. Tu biblioteca está más cerca del ideal.
nehem
0

No es una respuesta directa a la pregunta del OP, pero está inspirada y puede ser útil para algunos. He creado una solución basada en objetos utilizando el __dict__código interno (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 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"
Hedde van der Heide
fuente
0

Una forma sencilla de obtener acceso a puntos (pero no acceso a matriz) es utilizar 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 úsalo así:

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

... para convertirlo en un dict:

>>> print(obj.__dict__)
{"key": "value"}
Emil Stenström
fuente
0

Esta solución es una mejora sobre la que ofrece epool para abordar el requisito del OP para acceder a los dictados anidados de manera consistente. La solución de epool no permitió acceder 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, ahora se puede hacer algo como: A.B.C.D.

deepak
fuente
0

Esto también funciona con dictados anidados y asegura que los dictados que se agregan más adelante se comporten de la misma manera:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__
Yaniv K.
fuente
0

La respuesta de @ derek73 es muy clara, pero no se puede encurtir ni copiar (en profundidad), y regresa Nonepor las teclas que faltan. El siguiente código soluciona esto.

Editar: No vi la respuesta anterior que aborda exactamente el mismo punto (votado a favor). Dejo la respuesta aquí como referencia.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)
marnix
fuente
-1

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))
Yonks Somarl
fuente