Estoy escribiendo mi propio contenedor, que necesita dar acceso a un diccionario dentro mediante llamadas de atributo. El uso típico del contenedor sería así:
dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo
Sé que podría ser estúpido escribir algo como esto, pero esa es la funcionalidad que necesito proporcionar. Estaba pensando en implementar esto de la siguiente manera:
def __getattribute__(self, item):
try:
return object.__getattribute__(item)
except AttributeError:
try:
return self.dict[item]
except KeyError:
print "The object doesn't have such attribute"
No estoy seguro de si los bloques try / except anidados son una buena práctica, por lo que otra forma sería usar hasattr()
y has_key()
:
def __getattribute__(self, item):
if hasattr(self, item):
return object.__getattribute__(item)
else:
if self.dict.has_key(item):
return self.dict[item]
else:
raise AttributeError("some customised error")
O para usar uno de ellos y un bloque de captura como este:
def __getattribute__(self, item):
if hasattr(self, item):
return object.__getattribute__(item)
else:
try:
return self.dict[item]
except KeyError:
raise AttributeError("some customised error")
¿Qué opción es más pitónica y elegante?
if 'foo' in dict_container:
. Amén.Respuestas:
Tu primer ejemplo está perfectamente bien. Incluso los documentos oficiales de Python recomiendan este estilo conocido como EAFP .
Personalmente, prefiero evitar el anidamiento cuando no es necesario:
PD.
has_key()
ha quedado en desuso durante mucho tiempo en Python 2. Use en suitem in self.dict
lugar.fuente
return object.__getattribute__(item)
es incorrecto y producirá unTypeError
porque se pasa el número incorrecto de argumentos. En cambio debería serreturn object.__getattribute__(self, item)
.from None
en la última línea?Si bien en Java es una mala práctica usar Excepciones para el control de flujo (principalmente porque las excepciones obligan a jvm a reunir recursos ( más aquí )), en Python tiene 2 principios importantes: Duck Typing y EAFP . Básicamente, esto significa que se le recomienda que intente usar un objeto de la forma en que cree que funcionaría, y manejarlo cuando las cosas no sean así.
En resumen, el único problema sería que su código se sangrara demasiado. Si lo desea, intente simplificar algunos de los anidamientos como lqc sugirió
fuente
Para su ejemplo específico, en realidad no necesita anidarlos. Si la expresión en el
try
bloque tiene éxito, la función volverá, por lo que cualquier código después del bloque try / except completo solo se ejecutará si falla el primer intento. Entonces solo puedes hacer:Anidarlos no es malo, pero siento que dejarlo plano hace que la estructura sea más clara: estás probando secuencialmente una serie de cosas y devolviendo la primera que funciona.
Por cierto, es posible que desee pensar si realmente desea usar en
__getattribute__
lugar de__getattr__
aquí. El uso__getattr__
simplificará las cosas porque sabrá que el proceso normal de búsqueda de atributos ya ha fallado.fuente
Solo tenga cuidado, en este caso primero
finally
se toca PERO también se omite.fuente
finally
bloques se ejecutan paraa(0)
, pero solofinally-return
se devuelve el padre .En mi opinión, esta sería la forma más pitónica de manejarlo, aunque y porque hace que su pregunta sea discutible. Tenga en cuenta que esto define en
__getattr__()
lugar de__getattribute__()
porque hacerlo significa que solo tiene que tratar con los atributos "especiales" que se mantienen en el diccionario interno.fuente
except
bloque puede generar resultados confusos en Python 3. Esto se debe a que (según PEP 3134) Python 3 rastrea la primera excepción (theKeyError
) como el "contexto" de la segunda excepción (theAttributeError
), y si alcanza el nivel superior, imprimirá un rastreo que incluye ambas excepciones. Esto puede ser útil cuando no se esperaba una segunda excepción, pero si está provocando deliberadamente la segunda excepción, no es deseable. Para Python 3.3, PEP 415 agregó la capacidad de suprimir el contexto mediante el usoraise AttributeError("whatever") from None
.KeyError
en unaAttributeError
y mostrando que es lo que ocurrió en un rastreo serían útiles y apropiadas.__getattr__
una excepción, el error probablemente sea un error tipográfico en el acceso al atributo, no un error de implementación en el código de la clase actual. Mostrar la excepción anterior como contexto puede confundir eso. E incluso cuando suprime el contexto conraise Whatever from None
, aún puede obtener la excepción anterior si es necesario a través deex.__context__
.__getattribute__()
.En Python es más fácil pedir perdón que permiso. No se preocupe por el manejo de excepciones anidado.
(Además,
has*
casi siempre usa excepciones bajo la cubierta de todos modos).fuente
Según la documentación , es mejor manejar múltiples excepciones a través de tuplas o de esta manera:
fuente
Un buen y simple ejemplo para try / except anidado podría ser el siguiente:
Ahora prueba varias combinaciones y obtendrás el resultado correcto:
[por supuesto, tenemos numpy, así que no necesitamos crear esta función]
fuente
Una cosa que me gusta evitar es plantear una nueva excepción al manejar una antigua. Hace que los mensajes de error sean confusos de leer.
Por ejemplo, en mi código, originalmente escribí
Y recibí este mensaje.
Lo que quería era esto:
No afecta cómo se manejan las excepciones. En cualquier bloque de código, se habría detectado un KeyError. Esto es simplemente una cuestión de obtener puntos de estilo.
fuente
from None
embargo, vea el uso de aumento de la respuesta aceptada para obtener aún más puntos de estilo. :)Si try-except-finally está anidado dentro del bloque finalmente, el resultado de "hijo" finalmente se conserva. Todavía no he encontrado la expansión oficial, pero el siguiente fragmento de código muestra este comportamiento en Python 3.6.
fuente
No creo que sea cuestión de ser pitónico o elegante. Se trata de evitar excepciones tanto como sea posible. Las excepciones están destinadas a manejar errores que pueden ocurrir en código o eventos sobre los que no tiene control. En este caso, tiene control total al verificar si un elemento es un atributo o en un diccionario, por lo tanto, evite las excepciones anidadas y continúe con su segundo intento.
fuente