Me parece más conveniente acceder a las claves dict como en obj.foo
lugar de obj['foo']
, así que escribí este fragmento:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
Sin embargo, supongo que debe haber alguna razón por la cual Python no proporciona esta funcionalidad de fábrica. ¿Cuáles serían las advertencias y las dificultades de acceder a las claves dict de esta manera?
python
dictionary
syntax
attributes
Izz ad-Din Ruhulessin
fuente
fuente
collections.namedtuple
Es muy útil para esto.easydict.EasyDict
Respuestas:
La mejor manera de hacer esto es:
Algunos pros:
.keys()
funcionan bien. A menos que, por supuesto, les asignes algún valor, ver más abajo)AttributeError
lugar deKeyError
Contras:
.keys()
voluntad no funcionará bien si consiguen sobrescritos por los datos entrantesE1123(unexpected-keyword-arg)
yE1103(maybe-no-member)
Una breve explicación de cómo funciona esto
__dict__
.__dict__
deba ser "solo un simple dict", por lo que podemos asignar cualquier subclasedict()
al diccionario interno.AttrDict()
instancia que estamos creando instancias (tal como estamos__init__
).super()
al__init__()
método nos aseguramos de que (ya) se comporte exactamente como un diccionario, ya que esa función llama a todo el código de instanciación del diccionario .Una de las razones por las que Python no proporciona esta funcionalidad fuera de la caja
Como se señaló en la lista "contras", esto combina el espacio de nombres de las claves almacenadas (¡que pueden provenir de datos arbitrarios y / o no confiables!) Con el espacio de nombres de los atributos del método dict incorporado. Por ejemplo:
fuente
.
, no puede romper las reglas del lenguaje :) Y no me gustaríaAttrDict
convertir automáticamente los campos que contienen espacio en algo diferente.Puede tener todos los caracteres de cadena legales como parte de la clave si usa la notación de matriz. Por ejemplo,
obj['!#$%^&*()_']
fuente
What would be the caveats and pitfalls of accessing dict keys in this manner?
(como atributos), y la respuesta es que la mayoría de los caracteres que se muestran aquí no serían utilizables.De esta otra pregunta SO hay un gran ejemplo de implementación que simplifica su código existente. Qué tal si:
Mucho más concisa y no deja ningún espacio para cruft adicional metan en sus
__getattr__
y__setattr__
funciones en el futuro.fuente
d = AttributeDict(foo=1)
.d.bar = 1
el atributo de barra se almacena dentro del atributo dict pero no en el propio dict. la impresiónd
muestra solo el elemento foo.d = AttributeDict(foo=1);d.bar = 1;print d
=> ¡{'foo': 1, 'bar': 1}
Funciona para mí!__getattr__
método que genere un valorAttributeError
si el atributo dado no existe, de lo contrario, cosas comogetattr(obj, attr, default_value)
no funcionan (es decir, no regresadefault_value
siattr
no existeobj
)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!"
Si
d
se instancia oKEY
se define od[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 cambiad.spam
), ahora obtiene: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,
es legal, pero
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
overspam['eggs']
(creo que se ve más limpia), y realmente comencé a desear esta funcionalidad cuando conocí elnamedtuple
. Pero la conveniencia de poder hacer lo siguiente lo supera.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 .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:
En donde actualizo, para responder una pregunta de seguimiento en los comentarios
En los comentarios (abajo), Elmo pregunta:
Si bien nunca he usado este caso de uso (nuevamente, tiendo a usar anidado
dict
, por coherencia), el siguiente código funciona:fuente
Advertencia: Por algunas razones, clases como esta parecen romper el paquete de multiprocesamiento. Acabo de luchar con este error por un tiempo antes de encontrar este SO: Encontrar una excepción en el multiprocesamiento de Python
fuente
Puede extraer una clase de contenedor conveniente de la biblioteca estándar:
para evitar tener que copiar bits de código. No hay acceso estándar al diccionario, pero es fácil recuperarlo si realmente lo desea. El código en argparse es simple,
fuente
types.SimpleNamespace
docs.python.org/dev/library/types.html#types.SimpleNamespace¿Qué pasa si desea una clave que es un método, como
__eq__
o__getattr__
?Y no podría tener una entrada que no comenzara con una letra, por lo que usar
0343853
como clave está desactivado.¿Y si no quisieras usar una cuerda?
fuente
pickle.dump
usos__getstate__
Las tuplas se pueden utilizar para dictar claves. ¿Cómo accederías a la tupla en tu construcción?
Además, namedtuple es una estructura conveniente que puede proporcionar valores a través del acceso al atributo.
fuente
¿Qué tal Prodict , la pequeña clase de Python que escribí para gobernarlos a todos :)
Además, obtienes la finalización automática del código , las instancias recursivas de objetos y la conversión automática de tipos .
Puede hacer exactamente lo que pidió:
Ejemplo 1: tipo de sugerencia
Ejemplo 2: conversión de tipo automática
fuente
No funciona en general. No todas las claves dict válidas hacen atributos direccionables ("la clave"). Entonces, deberás tener cuidado.
Los objetos de Python son básicamente diccionarios. Así que dudo que haya mucho rendimiento u otra penalización.
fuente
Esto no aborda la pregunta original, pero debería ser útil para las personas que, como yo, terminan aquí cuando buscan una biblioteca que brinde esta funcionalidad.
Adicto es una gran lib para esto: https://github.com/mewwts/addict se ocupa de muchas de las preocupaciones mencionadas en las respuestas anteriores.
Un ejemplo de los documentos:
Con adicto:
fuente
Me pregunté cuál era el estado actual de "dict keys as attr" en el ecosistema de python. Como han señalado varios comentaristas, esto probablemente no sea algo que desee rodar desde cero , ya que hay varias trampas y pistolas, algunas de ellas muy sutiles. Además, no recomendaría usar
Namespace
como clase base, he estado en ese camino, no es bonito.Afortunadamente, hay varios paquetes de código abierto que proporcionan esta funcionalidad, ¡listos para instalar pip! Lamentablemente, hay varios paquetes. Aquí hay una sinopsis, a partir de diciembre de 2019.
Contenders (compromiso más reciente para master | #commits | #contribs | cobertura%):
Ya no se mantiene o no se mantiene:
Actualmente recomiendo munch o adicto . Tienen la mayor cantidad de commits, contribuyentes y lanzamientos, lo que sugiere una base de código de código abierto saludable para cada uno. Tienen el readme.md de aspecto más limpio, 100% de cobertura y un buen conjunto de pruebas.
No tengo un perro en esta carrera (¡por ahora!), Además de haber lanzado mi propio código dict / attr y haber perdido un montón de tiempo porque no estaba al tanto de todas estas opciones :). Puedo contribuir a adicto / munch en el futuro, ya que preferiría ver un paquete sólido que un montón de fragmentados. Si te gustan, ¡contribuye! En particular, parece que munch podría usar una insignia de codecov y el adicto podría usar una insignia de versión de python.
pros adictos:
adicto contras:
typing.Dict
si tufrom addict import Dict
munch pros:
munch contras:
En donde publico
Hace muchas lunas, cuando usaba editores de texto para escribir python, en proyectos solo con mí u otro desarrollador, me gustaba el estilo de dict-attrs, la capacidad de insertar claves simplemente declarando
foo.bar.spam = eggs
. Ahora trabajo en equipos y uso un IDE para todo, y me he alejado de este tipo de estructuras de datos y tipeo dinámico en general, a favor del análisis estático, las técnicas funcionales y las sugerencias de tipo. Comencé a experimentar con esta técnica, subclasificando Pstruct con objetos de mi propio diseño:Esto le da un objeto que todavía se comporta como un dict, pero también le permite acceder a claves como atributos, de una manera mucho más rígida. La ventaja aquí es que yo (o los desafortunados consumidores de su código) sé exactamente qué campos pueden y no pueden existir, y el IDE puede completar automáticamente los campos. Subclasificar también vainilla
dict
significa que la serialización json es fácil. Creo que la próxima evolución en esta idea sería un generador de prototipos personalizado que emita estas interfaces, y un buen efecto adicional es que obtienes estructuras de datos en varios idiomas e IPC a través de gRPC de forma casi gratuita.Si decide ir con atributos, es esencial documentar qué campos se esperan, para su propia cordura (y la de sus compañeros de equipo).
¡Siéntase libre de editar / actualizar esta publicación para mantenerla reciente!
fuente
addict
es que no generará excepciones cuando escribe mal un atributo, ya que devolverá uno nuevoDict
(esto es necesario para que foo.abc = 'bar' funcione).Aquí hay un breve ejemplo de registros inmutables que usan incorporado
collections.namedtuple
:y un ejemplo de uso:
fuente
Solo para agregar algo de variedad a la respuesta, sci-kit learn lo ha implementado como
Bunch
:Todo lo que necesita es obtener los métodos
setattr
ygetattr
: lasgetattr
comprobaciones de las claves dict y los movimientos para verificar los atributos reales. Estasetstaet
es una solución para solucionar los "racimos" de decapado / deshebrado: si no está interesado, consulte https://github.com/scikit-learn/scikit-learn/issues/6196fuente
No es necesario escribir el suyo ya que ya existen setattr () y getattr ().
La ventaja de los objetos de clase probablemente entra en juego en la definición de clase y la herencia.
fuente
Creé esto basado en la entrada de este hilo. Sin embargo, necesito usar odict, así que tuve que anular get y set attr. Creo que esto debería funcionar para la mayoría de los usos especiales.
El uso se ve así:
La clase:
Este es un patrón bastante bueno ya mencionado en el hilo, pero si solo desea tomar un dict y convertirlo en un objeto que funcione con autocompletar en un IDE, etc.
fuente
Aparentemente, ahora hay una biblioteca para esto: https://pypi.python.org/pypi/attrdict , que implementa esta funcionalidad exacta más la fusión recursiva y la carga json. Podría valer la pena echarle un vistazo.
fuente
Esto es lo que uso
fuente
namedtuple('Args', list(args.keys()))(**args)
Puedes hacerlo usando esta clase que acabo de hacer. Con esta clase, puede usar el
Map
objeto como otro diccionario (incluida la serialización json) o con la notación de puntos. Espero ayudarteEjemplos de uso:
fuente
dict
métodos, por ejemplo:m=Map(); m["keys"] = 42; m.keys()
giveTypeError: 'int' object is not callable
.field/attribute
y no unmethod
, pero si asigna un método en su lugar un número con el que puede acceder a ese métodom.method()
.Permítanme publicar otra implementación, que se basa en la respuesta de Kinvais, pero integra ideas del AttributeDict propuesto en http://databio.org/posts/python_AttributeDict.html .
La ventaja de esta versión es que también funciona para diccionarios anidados:
fuente
fuente
La solución es:
fuente
Como sugiere @Henry, una de las razones por las cuales el acceso punteado no se puede usar en los dictos es que limita los nombres de clave dict a variables válidas para python, restringiendo así todos los nombres posibles.
Los siguientes son ejemplos de por qué el acceso de puntos no sería útil en general, dada una sentencia
d
:Validez
Los siguientes atributos serían inválidos en Python:
Estilo
Las convenciones de PEP8 impondrían una restricción suave en la denominación de atributos:
A. Nombres de palabras clave reservadas (o función incorporada):
B. La regla del caso sobre métodos y nombres de variables :
A veces, estas inquietudes se plantean en bibliotecas como los pandas , lo que permite el acceso punteado de las columnas de DataFrame por nombre. El mecanismo predeterminado para resolver las restricciones de nomenclatura es también la notación de matriz, una cadena entre paréntesis.
Si estas restricciones no se aplican a su caso de uso, hay varias opciones en las estructuras de datos de acceso punteado .
fuente
Puedes usar dict_to_obj https://pypi.org/project/dict-to-obj/ Hace exactamente lo que pediste
fuente
.idea
y cualquier archivo generado por IDE o específico del usuario en su.gitignore
.Esta no es una 'buena' respuesta, pero pensé que era ingeniosa (no maneja los dictados anidados en la forma actual). Simplemente envuelva su dict en una función:
Ahora tiene una sintaxis ligeramente diferente. Para acceder a los elementos dictados como lo hacen los atributos
f.key
. Para acceder a los elementos dict (y otros métodos dict) de la manera habitual, hagamosf()['key']
y podamos actualizar convenientemente el dict llamando a f con argumentos de palabras clave y / o un diccionarioEjemplo
Y ahí está. Seré feliz si alguien sugiere beneficios e inconvenientes de este método.
fuente
Como señaló Doug, hay un paquete Bunch que puede usar para lograr la
obj.key
funcionalidad. En realidad hay una versión más nueva llamadaNeoBunch
Sin embargo, tiene una gran característica para convertir su dict en un objeto NeoBunch a través de su función neobunchify . Utilizo mucho las plantillas de Mako y pasar datos como objetos NeoBunch los hace mucho más legibles, por lo que si terminas usando un dict normal en tu programa Python pero quieres la notación de puntos en una plantilla de Mako, puedes usarlo de esa manera:
Y la plantilla de Mako podría verse así:
fuente
La forma más fácil es definir una clase, llamémosla Espacio de nombres. que usa el objeto dict .update () en el dict. Entonces, el dict será tratado como un objeto.
fuente