¿Es mejor "probar" algo y detectar la excepción o probar si es posible primero evitar una excepción?
¿Debo probar if
que algo es válido o simplemente try
hacerlo y detectar la excepción?
- ¿Existe alguna documentación sólida que indique que se prefiere una forma?
- ¿ Es una forma más pitónica ?
Por ejemplo, ¿debería:
if len(my_list) >= 4:
x = my_list[3]
else:
x = 'NO_ABC'
O:
try:
x = my_list[3]
except IndexError:
x = 'NO_ABC'
Algunas reflexiones...
PEP 20 dice:
Los errores nunca deben pasar en silencio.
A menos que se silencie explícitamente.
¿Debería interpretarse el uso de a try
en lugar de an como un error que se transmite en silencio? if
Y si es así, ¿lo está silenciando explícitamente al usarlo de esta manera y, por lo tanto, está bien?
No me refiero a situaciones en las que sólo puedes hacer las cosas de una manera; Por ejemplo:
try:
import foo
except ImportError:
import baz
Deberías preferirlo try/except
si if/else
eso resulta en
- aceleraciones (por ejemplo, evitando búsquedas adicionales)
- código más limpio (menos líneas/más fácil de leer)
A menudo, estos van de la mano.
aceleraciones
En el caso de intentar encontrar un elemento en una lista larga mediante:
try:
x = my_list[index]
except IndexError:
x = 'NO_ABC'
intentarlo, excepto es la mejor opción cuando index
probablemente esté en la lista y el IndexError generalmente no se genera. De esta manera evitará la necesidad de realizar una búsqueda adicional mediante if index < len(my_list)
.
Python fomenta el uso de excepciones, que manejas es una frase de Dive Into Python . Su ejemplo no solo maneja la excepción (elegantemente), en lugar de dejarla pasar silenciosamente , sino que también la excepción ocurre solo en el caso excepcional de que no se encuentre el índice (¡de ahí la palabra excepción !).
código más limpio
La documentación oficial de Python menciona EAFP : es más fácil pedir perdón que permiso y Rob Knight señala que detectar errores en lugar de evitarlos puede resultar en un código más limpio y más fácil de leer. Su ejemplo lo dice así:
Peor (LBYL 'mira antes de saltar') :
#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
return None
elif len(s) > 10: #too many digits for int conversion
return None
else:
return int(s)
Mejor (EAFP: Es más fácil pedir perdón que permiso) :
try:
return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
return None
En este caso particular, deberías usar algo completamente diferente:
x = myDict.get("ABC", "NO_ABC")
Sin embargo, en general: si espera que la prueba falle con frecuencia, utilice if
. Si la prueba es costosa en comparación con simplemente intentar la operación y detectar la excepción si falla, use try
. Si ninguna de estas condiciones se aplica, elija la que le resulte más fácil.
Siempre se debe usar try
y except
directamente en lugar de dentro de una if
protección si existe alguna posibilidad de una condición de carrera. Por ejemplo, si desea asegurarse de que exista un directorio, no haga esto:
import os, sys
if not os.path.isdir('foo'):
try:
os.mkdir('foo')
except OSError, e
print e
sys.exit(1)
Si otro hilo o proceso crea el directorio entre isdir
y mkdir
, saldrás. En su lugar, haz esto:
import os, sys, errno
try:
os.mkdir('foo')
except OSError, e
if e.errno != errno.EEXIST:
print e
sys.exit(1)
Eso solo saldrá si no se puede crear el directorio 'foo'.
Si es trivial comprobar si algo fallará antes de hacerlo, probablemente debería favorecerlo. Después de todo, crear excepciones (incluidos sus rastreos asociados) lleva tiempo.
Se deben utilizar excepciones para:
- cosas que son inesperadas, o...
- cosas en las que necesitas saltar más de un nivel de lógica (por ejemplo, donde a
break
no te lleva lo suficientemente lejos), o... - cosas en las que no sabes exactamente qué va a manejar la excepción de antemano, o...
- cosas en las que comprobar con antelación si hay fallos es caro (en comparación con simplemente intentar la operación)
Tenga en cuenta que, a menudo, la respuesta real es "ninguna"; por ejemplo, en su primer ejemplo, lo que realmente debe hacer es usar .get()
para proporcionar un valor predeterminado:
x = myDict.get('ABC', 'NO_ABC')