Estoy trabajando en el libro "Head First Python" (es mi idioma para aprender este año) y llegué a una sección en la que discuten sobre dos técnicas de código:
Verificar primero frente a manejo de excepciones.
Aquí hay una muestra del código Python:
# Checking First
for eachLine in open("../../data/sketch.txt"):
if eachLine.find(":") != -1:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
# Exception handling
for eachLine in open("../../data/sketch.txt"):
try:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
except:
pass
El primer ejemplo trata directamente con un problema en la .split
función. El segundo simplemente deja que el manejador de excepciones lo solucione (e ignora el problema).
Argumentan en el libro que usan el manejo de excepciones en lugar de verificar primero. El argumento es que el código de excepción detectará todos los errores, donde la comprobación primero solo detectará las cosas en las que piensa (y omite los casos de esquina). Primero me enseñaron a verificar, así que mi instinto inicial fue hacer eso, pero su idea es interesante. Nunca había pensado en usar el manejo de excepciones para tratar casos.
¿Cuál de las dos es generalmente considerada la mejor práctica?
fuente
if -1 == eachLine.find(":"): continue
entonces el resto del ciclo tampoco estaría sangrado.Respuestas:
En .NET, es una práctica común evitar el uso excesivo de excepciones. Un argumento es el rendimiento: en .NET, lanzar una excepción es computacionalmente costoso.
Otra razón para evitar su uso excesivo es que puede ser muy difícil leer el código que depende demasiado de ellos. La entrada de blog de Joel Spolsky hace un buen trabajo al describir el problema.
En el corazón del argumento está la siguiente cita:
Personalmente, lanzo excepciones cuando mi código no puede hacer lo que está contratado. Tiendo a usar try / catch cuando estoy a punto de lidiar con algo fuera del límite de mi proceso, por ejemplo, una llamada SOAP, una llamada a la base de datos, un archivo IO o una llamada al sistema. De lo contrario, intento codificar defensivamente. No es una regla difícil y rápida, pero es una práctica general.
Scott Hanselman también escribe sobre excepciones en .NET aquí . En este artículo describe varias reglas generales con respecto a las excepciones. ¿Mi favorito?
fuente
En Python en particular, generalmente se considera una mejor práctica detectar la excepción. Suele llamarse más fácil pedir perdón que permiso (EAFP), en comparación con Look Before You Leap (LBYL). Hay casos en los que LBYL le dará errores sutiles en algunos casos.
Sin embargo, tenga cuidado con las
except:
declaraciones simples , así como las declaraciones generales, excepto las declaraciones, ya que ambas también pueden enmascarar errores; algo como esto sería mejor:fuente
try
/ enexcept
lugar de configurar un condicional para "es probable que la siguiente arroje una excepción", y la práctica de Python es definitivamente preferir la primera. No hace daño que ese enfoque sea (probablemente) más eficiente aquí, ya que, en el caso de que la división tenga éxito, solo camina la cadena una vez. En cuanto a sisplit
debería lanzar una excepción aquí, diría que definitivamente debería hacerlo: una regla común es que debe lanzar cuando no puede hacer lo que dice su nombre y no puede dividirse en un delimitador faltante.Un enfoque pragmático
Deberías estar a la defensiva pero hasta cierto punto. Debería escribir manejo de excepciones pero hasta cierto punto. Voy a usar la programación web como ejemplo porque aquí es donde vivo.
Esto se debe a la experiencia de trabajar en escenarios de equipos grandes.
Una analogía
Imagínese si usara un traje espacial dentro de la ISS TODO el tiempo. Sería difícil ir al baño o comer, en absoluto. Sería súper voluminoso dentro del módulo espacial moverse. Sería una mierda. Escribir un montón de intentos de captura dentro de su código es algo así. Debes tener un punto en el que digas, hey, aseguré la EEI y mis astronautas están bien, así que no es práctico usar un traje espacial para cada escenario que pueda suceder.
fuente
El argumento principal del libro es que la versión de excepción del código es mejor porque detectará todo lo que podría haber pasado por alto si intenta escribir su propia comprobación de errores.
Creo que esta afirmación es cierta solo en circunstancias muy específicas, donde no le importa si el resultado es correcto.
No hay duda de que generar excepciones es una práctica segura y segura. Debe hacerlo siempre que sienta que hay algo en el estado actual del programa que usted (como desarrollador) no puede o no quiere tratar.
Su ejemplo, sin embargo, se trata de detectar excepciones. Si detecta una excepción, no se protege de escenarios que podría haber pasado por alto. Estás haciendo exactamente lo contrario: asumes que no has pasado por alto ningún escenario que podría haber causado este tipo de excepción y, por lo tanto, estás seguro de que está bien atraparlo (y así evitar que haga que el programa salga, como lo haría cualquier excepción no descubierta).
Usando el enfoque de excepción, si ve una
ValueError
excepción, omite una línea. Usando el enfoque tradicional sin excepción, cuenta el número de valores devueltossplit
y, si es menor que 2, omite una línea. ¿Debería sentirse más seguro con el enfoque de excepción, ya que puede haber olvidado algunas otras situaciones de "error" en su verificación de error tradicional y lasexcept ValueError
detectaría por usted?Esto depende de la naturaleza de su programa.
Si está escribiendo, por ejemplo, un navegador web o un reproductor de video, un problema con las entradas no debería causar que se bloquee con una excepción no detectada. Es mucho mejor generar algo remotamente sensible (incluso si, estrictamente hablando, incorrecto) que renunciar.
Si está escribiendo una aplicación donde la corrección es importante (como software comercial o de ingeniería), este sería un enfoque terrible. Si olvidó algún escenario que surge
ValueError
, lo peor que puede hacer es ignorar en silencio este escenario desconocido y simplemente omitir la línea. Así es como los errores muy sutiles y costosos terminan en el software.Puede pensar que la única forma en que puede ver
ValueError
en este código es sisplit
devuelve un solo valor (en lugar de dos). Pero, ¿quéprint
pasa si su declaración luego comienza a usar una expresión que surgeValueError
bajo ciertas condiciones? Esto hará que omita algunas líneas no porque se pierdan:
, sino porqueprint
fallan en ellas. Este es un ejemplo de un error sutil al que me refería anteriormente: no notarías nada, solo perderías algunas líneas.Mi recomendación es evitar capturar (¡pero no aumentar!) Excepciones en el código donde producir resultados incorrectos es peor que salir. La única vez que detectaría una excepción en dicho código es cuando tengo una expresión verdaderamente trivial, por lo que puedo razonar fácilmente qué puede causar cada uno de los posibles tipos de excepción.
En cuanto al impacto en el rendimiento del uso de excepciones, es trivial (en Python) a menos que se encuentren excepciones con frecuencia.
Si usa excepciones para manejar condiciones que ocurren de manera rutinaria, en algunos casos puede pagar un costo de rendimiento enorme. Por ejemplo, suponga que ejecuta remotamente algún comando. Puede verificar que el texto de su comando pase al menos la validación mínima (por ejemplo, sintaxis). O puede esperar a que se genere una excepción (lo que ocurre solo después de que el servidor remoto analiza su comando y encuentra un problema con él). Obviamente, el primero es órdenes de magnitud más rápido. Otro ejemplo simple: puede verificar si un número es cero ~ 10 veces más rápido que intentar ejecutar la división y luego detectar la excepción ZeroDivisionError.
Estas consideraciones solo importan si con frecuencia envía cadenas de comandos con formato incorrecto a servidores remotos o recibe argumentos de valor cero que utiliza para la división.
Nota: Asumo que usaría
except ValueError
en lugar de los justosexcept
; como otros señalaron, y como el libro mismo dice en unas pocas páginas, nunca debes usar desnudoexcept
.Otra nota: el enfoque adecuado sin excepción es contar el número de valores devueltos por
split
, en lugar de buscar:
. Este último es demasiado lento, ya que repite el trabajo realizadosplit
y puede casi duplicar el tiempo de ejecución.fuente
Como regla general, si sabe que una declaración podría generar un resultado no válido, pruébelo y trátelo. Use excepciones para cosas que no espera; cosas que son "excepcionales". Aclara el código en un sentido contractual ("no debe ser nulo" como ejemplo).
fuente
Usa lo que funciona bien en ...
Tanto el manejo de excepciones como la programación defensiva son formas diferentes de expresar la misma intención.
fuente
TBH, no importa si usa la
try/except
mecánica o unaif
verificación de estado de cuenta. Comúnmente se ven tanto EAFP como LBYL en la mayoría de las líneas de base de Python, siendo EAFP un poco más común. A veces, EAFP es mucho más legible / idiomático, pero en este caso particular, creo que está bien de cualquier manera.Sin embargo...
Tendría cuidado al usar su referencia actual. Un par de problemas evidentes con su código:
with
idioma al leer archivos en Python: hay muy pocas excepciones. Este no es uno de ellos.except
declaración simple que no detecta una excepción específica)role, lineSpoken = eachLine.split(":",1)
Ivc tiene una buena respuesta sobre esto y EAFP, pero también está filtrando el descriptor.
La versión LBYL no es necesariamente tan eficaz como la versión EAFP, por lo que decir que lanzar excepciones es "costoso en términos de rendimiento" es categóricamente falso. Realmente depende del tipo de cadenas que estés procesando:
fuente
Básicamente, el manejo de excepciones se supone que es más apropiado para los lenguajes OOP.
El segundo punto es el rendimiento, porque no tiene que ejecutar
eachLine.find
para cada línea.fuente
Creo que la programación defensiva perjudica el rendimiento. También debe detectar solo las excepciones que va a manejar, deje que el tiempo de ejecución se ocupe de la excepción que no sabe cómo manejar.
fuente