En donde respondo la pregunta que se hizo
¿Por qué Python no lo ofrece fuera de la caja?
Sospecho que tiene que ver con el Zen de Python : "Debería haber una, y preferiblemente solo una, forma obvia de hacerlo". Esto crearía dos formas obvias de acceder a los valores de los diccionarios: obj['key']
yobj.key
.
Advertencias y trampas
Estos incluyen la posible falta de claridad y confusión en el código. es decir, el siguiente podría ser confuso para alguien otra cosa que va a mantener su código en una fecha posterior, o incluso a usted, si usted no va de nuevo en ella por un tiempo. Nuevamente, de Zen : "¡La legibilidad cuenta!"
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Si d
se instancia o KEY
se define o d[KEY]
se asigna lejos de donded.spam
se está usando, puede fácilmente generar confusión sobre lo que se está haciendo, ya que este no es un idioma comúnmente usado. Sé que podría confundirme.
Además, si cambia el valor de la KEY
siguiente manera (pero no cambia d.spam
), ahora obtiene:
>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'
OMI, no vale la pena el esfuerzo.
Otros elementos
Como otros han señalado, puede usar cualquier objeto hashable (no solo una cadena) como clave dict. Por ejemplo,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
es legal, pero
>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
File "<stdin>", line 1
d.(2, 3)
^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>>
no es. Esto le da acceso a toda la gama de caracteres imprimibles u otros objetos hashaable para sus claves de diccionario, que no tiene al acceder a un atributo de objeto. Esto hace posible tal magia como una metaclase de objeto en caché, como la receta del Libro de cocina de Python (Capítulo 9) .
En donde publico
Prefiero la estética de spam.eggs
over spam['eggs']
(creo que se ve más limpia), y realmente comencé a desear esta funcionalidad cuando conocí el namedtuple
. Pero la conveniencia de poder hacer lo siguiente lo supera.
>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>
Este es un ejemplo simple, pero con frecuencia me encuentro usando dictos en situaciones diferentes de las que usaría la obj.key
notación (es decir, cuando necesito leer preferencias de un archivo XML). En otros casos, donde tengo la tentación de crear una instancia de una clase dinámica y aplicar algunos atributos por razones estéticas, sigo usando un dict por coherencia para mejorar la legibilidad.
Estoy seguro de que el OP hace tiempo que resolvió esto a su satisfacción, pero si todavía quiere esta funcionalidad, le sugiero que descargue uno de los paquetes de pypi que lo proporciona:
Bunch es con el que estoy más familiarizado. Subclase dedict
, por lo que tiene toda esa funcionalidad.
AttrDict también parece que también es bastante bueno, pero no estoy tan familiarizado con él y no he revisado la fuente con tanto detalle como Bunch .
- Addict se mantiene activamente y proporciona acceso similar a un atributo y más.
- Como se señaló en los comentarios de Rotareti, Bunch ha quedado en desuso, pero hay una bifurcación activa llamada Munch .
Sin embargo, para mejorar la legibilidad de su código, le recomiendo que no mezcle sus estilos de notación. Si prefiere esta notación, simplemente debe crear una instancia de un objeto dinámico, agregarle sus atributos deseados y llamarlo un día:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
En donde actualizo, para responder una pregunta de seguimiento en los comentarios
En los comentarios (abajo), Elmo pregunta:
¿Qué pasa si quieres ir más profundo? (refiriéndose al tipo (...))
Si bien nunca he usado este caso de uso (nuevamente, tiendo a usar anidado dict
, por coherencia), el siguiente código funciona:
>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
... setattr(d, x, C())
... i = 1
... for y in 'one two three'.split():
... setattr(getattr(d, x), y, i)
... i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}
collections.namedtuple
Es muy útil para esto.