¿Son los bloques try/except anidados en Python una buena práctica de programación?
⏰ 6 minutos de lectura
Mical
Estoy escribiendo mi propio contenedor, que debe dar acceso a un diccionario interno mediante llamadas de atributos. El uso típico del contenedor sería así:
dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo
Sé que puede 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 uno intente atrapar un bloque como este:
Apreciaría a los dioses pitón proporcionando if 'foo' in dict_container:. Amén.
– gseattle
15 de febrero de 2019 a las 4:26
lqc
Tu primer ejemplo está perfectamente bien. Incluso la documentación oficial de Python recomienda este estilo conocido como EAFP.
Personalmente, prefiero evitar anidar cuando no es necesario:
def __getattribute__(self, item):
try:
return object.__getattribute__(item)
except AttributeError:
pass # Fallback to dict
try:
return self.dict[item]
except KeyError:
raise AttributeError("The object doesn't have such attribute") from None
PD. has_key() ha quedado en desuso durante mucho tiempo en Python 2. Use item in self.dict en cambio.
return object.__getattribute__(item) es incorrecto y producirá un TypeError porque se está pasando el número incorrecto de argumentos. en cambio, debería ser return object.__getattribute__(self, item).
– martineau
20 de junio de 2014 a las 1:05
PEP 20: plano es mejor que anidado.
– 0 _
25 de enero de 2015 a las 18:25
Lo que hace el from None significa en la última línea?
– Nicolás
27/03/2017 a las 23:10
@niklas Esencialmente suprime el contexto de excepción (“durante el manejo de esta excepción ocurrió otra excepción” -mensajes que). Mira aquí
– Kade
27 de febrero de 2018 a las 18:32
El hecho de que los documentos de Python recomienden anidar los intentos es una locura. Es obviamente un estilo horrendo. La forma correcta de manejar una cadena de operaciones donde las cosas pueden fallar sería usar algún tipo de construcción monádica, que Python no admite.
– Henry Henrison
18 sep 2019 a las 18:43
bruno penteado
Mientras que en Java es una mala práctica usar excepciones para el control de flujo (principalmente porque las excepciones obligan a la JVM a recopilar recursos (más aquí)), en Python tiene dos principios importantes: pato escribiendo y EAFP. Básicamente, esto significa que se le anima a intentar 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 tuviera demasiada sangría. Si lo desea, intente simplificar algunos de los anidamientos, como sugirió lqc en la respuesta sugerida anteriormente.
Sławomir Lenart
Sólo tenga cuidado – en este caso el primero finally se toca, pero saltado también.
def a(z):
try:
100/z
except ZeroDivisionError:
try:
print('x')
finally:
return 42
finally:
return 1
In [1]: a(0)
x
Out[1]: 1
Vaya, me sorprende… ¿Podría indicarme algún fragmento de documentación que explique este comportamiento?
– Michal
28 de marzo de 2018 a las 13:56
@Michal: para tu información: ambos finally los bloques se ejecutan para a(0)pero solo padre finally-return es regresado.
– Sławomir Lenart
28 de marzo de 2018 a las 14:44
BrenBarn
Para su ejemplo específico, en realidad no necesita anidarlos. Si la expresión en el try block tiene éxito, la función regresará, por lo que cualquier código después de todo el bloque try/except solo se ejecutará si falla el primer intento. Así que solo puedes hacer:
def __getattribute__(self, item):
try:
return object.__getattribute__(item)
except AttributeError:
pass
# execution only reaches here when try block raised AttributeError
try:
return self.dict[item]
except KeyError:
print "The object doesn't have such attribute"
Anidarlos no está mal, 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 utilizar __getattribute__ en vez de __getattr__ aquí. Usando __getattr__ simplificará las cosas porque sabrá que el proceso normal de búsqueda de atributos ya ha fallado.
Cuidado devastador
Un buen y simple ejemplo de intento/excepto anidado podría ser el siguiente:
import numpy as np
def divide(x, y):
try:
out = x/y
except:
try:
out = np.inf * x / abs(x)
except:
out = np.nan
finally:
return out
Ahora prueba varias combinaciones y obtendrás el resultado correcto:
(Por supuesto, tenemos NumPy, por lo que no necesitamos crear esta función).
En mi opinión, esta sería la forma más pitónica de manejarlo, aunque y porque hace que tu pregunta sea discutible. Tenga en cuenta que esto define __getattr__() en vez de __getattribute__() porque hacerlo significa que solo tiene que lidiar con los atributos “especiales” que se mantienen en el diccionario interno.
def __getattr__(self, name):
''' Only called when an attribute lookup in the "usual" places has failed. '''
try:
return self.my_dict[name]
except KeyError:
raise AttributeError("some customized error message")
Pedro Mortensen
De acuerdo a la documentaciónes mejor manejar múltiples excepciones a través de tuplas o así:
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except IOError as e:
print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
print "Could not convert data to an integer."
except:
print "Unexpected error: ", sys.exc_info()[0]
raise
Esta respuesta realmente no aborda la pregunta original, pero para cualquiera que la lea, tenga en cuenta que el “desnudo”, excepto que al final, es una idea terrible (por lo general), ya que detectará todo, incluido, por ejemplo, NameError & KeyboardInterrupt, ¡que generalmente no es lo que querías decir!
– perdió
10 de enero de 2019 a las 8:02
Dado que el código vuelve a generar la misma excepción justo después de la declaración de impresión, ¿es esto realmente un gran problema? En este caso, puede proporcionar más contexto sobre la excepción sin ocultarla. Si no volviera a subir, estaría totalmente de acuerdo, pero no creo que haya riesgo de ocultar una excepción que no tenía la intención de ocultar.
– Escala Nimbus
11 de junio de 2020 a las 21:25
¿Ha sido útil esta solución?
Tu feedback nos ayuda a saber si la solución es correcta y está funcionando. De esta manera podemos revisar y corregir el contenido.
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información
Apreciaría a los dioses pitón proporcionando
if 'foo' in dict_container:
. Amén.– gseattle
15 de febrero de 2019 a las 4:26