"No sé si es por ignorancia, pero no me gusta ese tipo de programación, ya que utiliza excepciones para realizar el control de flujo".
En el mundo de Python, el uso de excepciones para el control de flujo es común y normal.
Incluso los desarrolladores principales de Python usan excepciones para el control de flujo y ese estilo está fuertemente integrado en el lenguaje (es decir, el protocolo iterador usa StopIteration para señalar la terminación del bucle).
Además, el estilo try-except-se utiliza para evitar las condiciones de carrera inherentes a algunas de las construcciones de "mira antes de saltar" . Por ejemplo, probar os.path.exists da como resultado información que puede estar desactualizada para cuando la use. Del mismo modo, Queue.full devuelve información que puede estar obsoleta. El estilo try-except-else producirá un código más confiable en estos casos.
"Entiendo que las excepciones no son errores, solo deben usarse para condiciones excepcionales"
En algunos otros idiomas, esa regla refleja sus normas culturales tal como se reflejan en sus bibliotecas. La "regla" también se basa en parte en consideraciones de rendimiento para esos idiomas.
La norma cultural de Python es algo diferente. En muchos casos, debe usar excepciones para control-flow. Además, el uso de excepciones en Python no ralentiza el código circundante y el código de llamada como lo hace en algunos lenguajes compilados (es decir, CPython ya implementa código para la verificación de excepciones en cada paso, independientemente de si realmente usa excepciones o no).
En otras palabras, su comprensión de que "las excepciones son para lo excepcional" es una regla que tiene sentido en otros idiomas, pero no para Python.
"Sin embargo, si está incluido en el lenguaje en sí, debe haber una buena razón para ello, ¿no?"
Además de ayudar a evitar las condiciones de carrera, las excepciones también son muy útiles para extraer el manejo de errores fuera de los bucles. Esta es una optimización necesaria en lenguajes interpretados que no tienden a tener un movimiento de código invariante de bucle automático .
Además, las excepciones pueden simplificar bastante el código en situaciones comunes donde la capacidad de manejar un problema está muy lejos de donde surgió el problema. Por ejemplo, es común tener un código de llamada de código de interfaz de usuario de nivel superior para la lógica empresarial que a su vez llama a rutinas de bajo nivel. Las situaciones que surgen en las rutinas de bajo nivel (como registros duplicados para claves únicas en accesos a bases de datos) solo se pueden manejar en el código de nivel superior (como solicitar al usuario una nueva clave que no entre en conflicto con las claves existentes). El uso de excepciones para este tipo de flujo de control permite que las rutinas de nivel medio ignoren completamente el problema y se desacoplen de ese aspecto del control de flujo.
Aquí hay una buena publicación de blog sobre la indispensabilidad de las excepciones .
Además, vea esta respuesta de desbordamiento de pila: ¿Son realmente excepciones para errores excepcionales?
"¿Cuál es la razón para que exista el try-except-else?"
La cláusula else es interesante. Se ejecuta cuando no hay excepción, pero antes de la cláusula finalmente. Ese es su propósito principal.
Sin la cláusula else, la única opción para ejecutar código adicional antes de la finalización sería la práctica torpe de agregar el código a la cláusula try. Eso es torpe porque corre el riesgo de generar excepciones en el código que no estaba destinado a ser protegido por el try-block.
El caso de uso de ejecutar código desprotegido adicional antes de la finalización no surge con mucha frecuencia. Por lo tanto, no espere ver muchos ejemplos en el código publicado. Es algo raro
Otro caso de uso para la cláusula else es realizar acciones que deben ocurrir cuando no se produce una excepción y que no ocurren cuando se manejan las excepciones. Por ejemplo:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Otro ejemplo ocurre en los corredores unittest:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
Por último, el uso más común de una cláusula else en un try-block es para un poco de embellecimiento (alineando los resultados excepcionales y los resultados no excepcionales en el mismo nivel de sangría). Este uso es siempre opcional y no es estrictamente necesario.
Un
try
bloque le permite manejar un error esperado. Elexcept
bloque solo debe capturar excepciones que esté preparado para manejar. Si maneja un error inesperado, su código puede hacer lo incorrecto y ocultar errores.Se
else
ejecutará una cláusula si no hubo errores, y al no ejecutar ese código en eltry
bloque, evitará detectar un error inesperado. Nuevamente, detectar un error inesperado puede ocultar errores.Ejemplo
Por ejemplo:
La suite "try, except" tiene dos cláusulas opcionales,
else
yfinally
. Entonces es en realidadtry-except-else-finally
.else
evaluará solo si no hay excepción deltry
bloque. Nos permite simplificar el código más complicado a continuación:así que si comparamos una
else
con la alternativa (que podría crear errores), vemos que reduce las líneas de código y podemos tener una base de código más legible, mantenible y con menos errores.finally
finally
se ejecutará sin importar qué, incluso si otra línea se está evaluando con una declaración de devolución.Desglosado con pseudocódigo
Podría ser útil desglosar esto, en la forma más pequeña posible que muestre todas las características, con comentarios. Suponga que este pseudocódigo está sintácticamente correcto (pero no se puede ejecutar a menos que se definan los nombres) en una función.
Por ejemplo:
Es cierto que podríamos incluir el código en el
else
bloque en eltry
bloque en su lugar, donde se ejecutaría si no hubiera excepciones, pero ¿y si ese código genera una excepción del tipo que estamos atrapando? Dejarlo en eltry
bloque escondería ese error.Queremos minimizar las líneas de código en el
try
bloque para evitar detectar excepciones que no esperábamos, bajo el principio de que si nuestro código falla, queremos que falle en voz alta. Esta es una mejor práctica .En Python, la mayoría de las excepciones son errores.
Podemos ver la jerarquía de excepciones usando pydoc. Por ejemplo, en Python 2:
o Python 3:
Nos dará la jerarquía. Podemos ver que la mayoría de los tipos
Exception
son errores, aunque Python usa algunos de ellos para cosas como finalizarfor
bucles (StopIteration
). Esta es la jerarquía de Python 3:Un comentarista preguntó:
No, no devuelve la excepción, simplemente vuelva a subirla con un desnudo
raise
para preservar el stacktrace.O, en Python 3, puede generar una nueva excepción y preservar la traza inversa con el encadenamiento de excepciones:
Elaboro mi respuesta aquí .
fuente
raise
encadenamiento simple o de excepción, pero eso es más sobre el tema y cubierto aquí: stackoverflow.com/q/2052390/541136 - Probablemente elimine estos comentarios después de haber visto que los has visto.Python no se suscribe a la idea de que las excepciones solo deben usarse para casos excepcionales, de hecho, el idioma es "pedir perdón, no permiso" . Esto significa que usar excepciones como parte rutinaria de su control de flujo es perfectamente aceptable y, de hecho, se recomienda.
En general, esto es algo bueno, ya que trabajar de esta manera ayuda a evitar algunos problemas (como un ejemplo obvio, a menudo se evitan las condiciones de carrera), y tiende a hacer que el código sea un poco más legible.
Imagine que tiene una situación en la que toma alguna entrada del usuario que necesita ser procesada, pero tiene un valor predeterminado que ya está procesado. La
try: ... except: ... else: ...
estructura hace que el código sea muy legible:Compare cómo podría funcionar en otros idiomas:
Tenga en cuenta las ventajas. No es necesario verificar que el valor sea válido y analizarlo por separado, se hacen una vez. El código también sigue una progresión más lógica, la ruta del código principal es la primera, seguida de 'si no funciona, haga esto'.
El ejemplo es, naturalmente, un poco artificial, pero muestra que hay casos para esta estructura.
fuente
La respuesta a esto es que depende del contexto. Si haces esto:
Demuestra que no conoces muy bien Python. Esta funcionalidad está encapsulada en el
dict.get
método:El bloque
try
/except
es una forma de escritura mucho más desordenada y visualmente desordenada que se puede ejecutar de manera eficiente en una sola línea con un método atómico. Hay otros casos donde esto es cierto.Sin embargo, eso no significa que debamos evitar todo manejo de excepciones. En algunos casos se prefiere evitar las condiciones de carrera. No compruebe si existe un archivo, solo intente abrirlo y capture el IOError apropiado. En aras de la simplicidad y la legibilidad, intente encapsular esto o factorizarlo como sea apropiado.
Lea el Zen de Python , entendiendo que hay principios que están en tensión, y desconfíe del dogma que se basa demasiado en cualquiera de las declaraciones en él.
fuente
Vea el siguiente ejemplo que ilustra todo sobre try-except-else-finally:
Impleméntalo y ven por:
fuente
Debe tener cuidado al usar el bloque finalmente, ya que no es lo mismo que usar un bloque else en el intento, excepto. El último bloque se ejecutará independientemente del resultado del intento, excepto.
Como todos han notado, el uso del bloque else hace que su código sea más legible y solo se ejecuta cuando no se produce una excepción
fuente
Cada vez que ves esto:
O incluso esto:
Considere esto en su lugar:
fuente
Solo porque nadie más ha publicado esta opinión, diría
A diferencia de las palabras clave
try
,except
yfinally
, el significado de laelse
cláusula no es evidente; Es menos legible. Debido a que no se usa con mucha frecuencia, hará que las personas que leen su código deseen verificar los documentos para asegurarse de que comprenden lo que está sucediendo.(Estoy escribiendo esta respuesta precisamente porque encontré un
try/except/else
en mi base de código y causó un momento wtf y me obligó a buscar en Google).Entonces, donde veo código como el ejemplo de OP:
Preferiría refactorizar a
<1> retorno explícito, muestra claramente que, en el caso de excepción, hemos terminado de trabajar
<2> como un agradable efecto secundario menor, el código que solía estar en el
else
bloque se ve afectado por un nivel.fuente
Este es mi fragmento simple sobre cómo entender el bloque try-except-else-finally en Python:
Probemos div 1/1:
Probemos div 1/0
fuente
OP, ERES CORRECTO. Lo demás después de intentar / excepto en Python es feo . conduce a otro objeto de control de flujo donde no se necesita ninguno:
Un equivalente totalmente claro es:
Esto es mucho más claro que una cláusula else. El resto después de try / except no se escribe con frecuencia, por lo que lleva un momento determinar cuáles son las implicaciones.
Solo porque PUEDES hacer algo, no significa que DEBES hacer algo.
Se han agregado muchas funciones a los idiomas porque alguien pensó que podría ser útil. El problema es que, entre más características, las cosas menos claras y obvias se deben a que la gente generalmente no usa esas campanas y silbatos.
Solo mis 5 centavos aquí. Tengo que ir detrás y limpiar un montón de código escrito por desarrolladores universitarios de primer año que piensan que son inteligentes y quieren escribir código de una manera súper ajustada y súper eficiente cuando eso solo lo hace un desastre para intentar leer / modificar más tarde. Yo voto por la legibilidad todos los días y dos veces los domingos.
fuente
print
declaración la que falle. ¿Qué sucede six = blah()
devuelve unstr
, pero su declaración de impresión esprint 'just succeeded with blah. x == %d' % x
? Ahora tienes unTypeError
ser generado donde no estás preparado para manejar uno; estás inspeccionandox = blah()
para encontrar la fuente de la excepción, y ni siquiera está allí. He hecho esto (o el equivalente) más de una vez dondeelse
me habría evitado cometer este error. Ahora lo se mejor. :-Delse
cláusula no es una declaración bonita, y hasta que no estés acostumbrado, no es intuitiva. Pero entonces, tampoco lo fuefinally
cuando empecé a usarlo ...else
cláusula no son capturadas por elexcept
.x = blah()
para encontrar la fuente de la excepción",traceback
¿ teniendo en cuenta por qué inspeccionarías la fuente de la excepción desde un lugar equivocado?