¿Cuál es el uso previsto de la else
cláusula opcional de la try
declaración?
¿Cuál es el uso previsto de la else
cláusula opcional de la try
declaración?
Respuestas:
Las declaraciones en el else
bloque se ejecutan si la ejecución cae al final de la try
- si no hubo excepción. Honestamente, nunca he encontrado una necesidad.
Sin embargo, las notas de manejo de excepciones :
El uso de la cláusula else es mejor que agregar código adicional a la cláusula try porque evita detectar accidentalmente una excepción que no fue generada por el código protegido por la declaración try ... except.
Entonces, si tiene un método que podría, por ejemplo, arrojar un IOError
, y desea detectar las excepciones, aumenta, pero hay algo más que desea hacer si la primera operación tiene éxito, y no desea detectar un IOError de esa operación, podrías escribir algo como esto:
try:
operation_that_can_throw_ioerror()
except IOError:
handle_the_exception_somehow()
else:
# we don't want to catch the IOError if it's raised
another_operation_that_can_throw_ioerror()
finally:
something_we_always_need_to_do()
Si solo coloca another_operation_that_can_throw_ioerror()
después operation_that_can_throw_ioerror
, except
detectaría los errores de la segunda llamada. Y si lo coloca después de todo el try
bloque, siempre se ejecutará, y no hasta después del finally
. El else
te permite asegurarte
finally
bloque, yIOError
s que surja no se ve atrapado aquíreturn
, continue
o break
.
Hay una gran razón para usar else
: estilo y legibilidad. En general, es una buena idea mantener el código que puede causar excepciones cerca del código que se ocupa de ellos. Por ejemplo, compare estos:
try:
from EasyDialogs import AskPassword
# 20 other lines
getpass = AskPassword
except ImportError:
getpass = default_getpass
y
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
else:
# 20 other lines
getpass = AskPassword
El segundo es bueno cuando except
no puede regresar temprano o volver a lanzar la excepción. De ser posible, habría escrito:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
return False # or throw Exception('something more descriptive')
# 20 other lines
getpass = AskPassword
Nota: La respuesta copiada del duplicado publicado recientemente aquí , de ahí todo este material "AskPassword".
Un uso: pruebe algún código que debería generar una excepción.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(Este código debe resumirse en una prueba más genérica en la práctica).
Python try-else
¿Cuál es el uso previsto de la
else
cláusula opcional de la declaración de prueba?
El uso previsto es tener un contexto para que se ejecute más código si no hubo excepciones donde se esperaba que se manejara.
Este contexto evita el manejo accidental de errores que no esperaba.
Pero es importante entender las condiciones precisas que causan la cláusula else a plazo, porque return
, continue
y break
puede interrumpir el flujo de control a else
.
La else
instrucción se ejecuta si hay no hay excepciones y si no es interrumpida por una return
, continue
o break
comunicado.
La
else
cláusula opcional se ejecuta siempre y cuando el control fluya fuera del final de latry
cláusula. *
(Se agrega negrita). Y la nota al pie dice:
* En la actualidad, el control “fluye fuera de la final”, excepto en el caso de una excepción o la ejecución de una
return
,continue
obreak
comunicado.
Requiere al menos una cláusula anterior excepto ( ver la gramática ). Entonces, realmente no es "try-else", es "try-except-else (-finally)", con el else
(y finally
) siendo opcional.
El Tutorial de Python explica el uso previsto:
La declaración try ... except tiene una cláusula else opcional, que, cuando está presente, debe seguir todas las cláusulas excepto. Es útil para el código que debe ejecutarse si la cláusula try no genera una excepción. Por ejemplo:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
El uso de la cláusula else es mejor que agregar código adicional a la cláusula try porque evita detectar accidentalmente una excepción que no fue generada por el código protegido por la declaración try ... except.
else
frente al código que sigue al try
bloqueSi maneja un error, el else
bloque no se ejecutará. Por ejemplo:
def handle_error():
try:
raise RuntimeError('oops!')
except RuntimeError as error:
print('handled a RuntimeError, no big deal.')
else:
print('if this prints, we had no error!') # won't print!
print('And now we have left the try block!') # will print!
Y ahora,
>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
Try-except-else es ideal para combinar el patrón EAFP con tipeo de pato :
try:
cs = x.cleanupSet
except AttributeError:
pass
else:
for v in cs:
v.cleanup()
Puede pensar que este código ingenuo está bien:
try:
for v in x.cleanupSet:
v.clenaup()
except AttributeError:
pass
Esta es una excelente manera de ocultar accidentalmente errores graves en su código. Escribí la limpieza allí, pero se está tragando el AttributeError que me dejaría saber. Peor aún, ¿qué pasaría si lo hubiera escrito correctamente, pero en ocasiones el método de limpieza pasaba un tipo de usuario que tenía un atributo mal nombrado, lo que provocaba un error silencioso a la mitad y dejaba un archivo sin cerrar? Buena suerte depurando eso.
Me parece realmente útil cuando tienes que hacer la limpieza, incluso si hay una excepción:
try:
data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
handle_exception(e)
else:
do_stuff(data)
finally:
clean_up()
A pesar de que no puedes pensar en un uso en este momento, puedes apostar que tiene que ser útil. Aquí hay una muestra poco imaginativa:
Con else
:
a = [1,2,3]
try:
something = a[2]
except:
print "out of bounds"
else:
print something
Sin else
:
try:
something = a[2]
except:
print "out of bounds"
if "something" in locals():
print something
Aquí tiene la variable something
definida si no se produce ningún error. Puede eliminar esto fuera del try
bloque, pero luego requiere una detección desordenada si se define una variable.
something = a[2]; print something
en el intento: bloquear?
Hay un buen ejemplo de try-else
en PEP 380 . Básicamente, se trata de hacer un manejo diferente de excepciones en diferentes partes del algoritmo.
Es algo como esto:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
Esto le permite escribir el código de manejo de excepciones más cerca de donde ocurre la excepción.
De errores y excepciones # Excepciones de manejo - docs.python.org
La
try ... except
declaración tiene unaelse
cláusula opcional que, cuando está presente, debe seguir a todas, excepto las cláusulas. Es útil para el código que debe ejecutarse si la cláusula try no genera una excepción. Por ejemplo:for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
El uso de la cláusula else es mejor que agregar código adicional a la cláusula try porque evita detectar accidentalmente una excepción que no fue generada por el código protegido por la declaración try ... except.
Mirando la referencia de Python , parece que else
se ejecuta después try
cuando no hay excepción. La cláusula else opcional se ejecuta si y cuando el control fluye fuera del final de la cláusula try. 2 Las excepciones en la cláusula else no son manejadas por las anteriores, excepto las cláusulas.
Sumérgete en Python tiene un ejemplo en el que, si entiendo correctamente, en el try
bloque intentan importar un módulo, cuando eso falla, obtienes una excepción y se vincula por defecto, pero cuando funciona tienes una opción para else
bloquear y vincular lo que se requiere (ver enlace para el ejemplo y explicación).
Si intentaste trabajar en catch
bloque, podría arrojar otra excepción: supongo que ahí es donde el else
bloque resulta útil.
try
bloque.
Eso es. El bloque 'else' de una cláusula try-except existe para el código que se ejecuta cuando (y solo cuando) la operación intentada tiene éxito. Se puede usar y se puede abusar de él.
try:
fp= open("configuration_file", "rb")
except EnvironmentError:
confdata= '' # it's ok if the file can't be opened
else:
confdata= fp.read()
fp.close()
# your code continues here
# working with (possibly empty) confdata
Personalmente, me gusta y lo uso cuando es apropiado. Agrupa semánticamente las declaraciones.
Quizás un uso podría ser:
#debug = []
def debuglog(text, obj=None):
" Simple little logger. "
try:
debug # does global exist?
except NameError:
pass # if not, don't even bother displaying
except:
print('Unknown cause. Debug debuglog().')
else:
# debug does exist.
# Now test if you want to log this debug message
# from caller "obj"
try:
if obj in debug:
print(text) # stdout
except TypeError:
print('The global "debug" flag should be an iterable.')
except:
print('Unknown cause. Debug debuglog().')
def myfunc():
debuglog('Made it to myfunc()', myfunc)
debug = [myfunc,]
myfunc()
Tal vez esto también te lleve a un uso.
He encontrado que la try: ... else:
construcción es útil en la situación en la que está ejecutando consultas de base de datos y registrando los resultados de esas consultas en una base de datos separada del mismo tipo / sabor. Digamos que tengo muchos subprocesos de trabajo que manejan consultas de bases de datos enviadas a una cola
#in a long running loop
try:
query = queue.get()
conn = connect_to_db(<main db>)
curs = conn.cursor()
try:
curs.execute("<some query on user input that may fail even if sanitized">)
except DBError:
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of failed query")
logcurs.close()
logconn.close()
else:
#we can't put this in main try block because an error connecting
#to the logging DB would be indistinguishable from an error in
#the mainquery
#We can't put this after the whole try: except: finally: block
#because then we don't know if the query was successful or not
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of successful query")
logcurs.close()
logconn.close()
#do something in response to successful query
except DBError:
#This DBError is because of a problem with the logging database, but
#we can't let that crash the whole thread over what might be a
#temporary network glitch
finally:
curs.close()
conn.close()
#other cleanup if necessary like telling the queue the task is finished
Por supuesto, si puede distinguir entre las posibles excepciones que se pueden generar, no tiene que usar esto, pero si el código que reacciona a un fragmento de código exitoso podría arrojar la misma excepción que el fragmento exitoso, y no puede simplemente deje pasar la segunda posible excepción, o regrese inmediatamente en caso de éxito (lo que mataría el hilo en mi caso), entonces esto es útil.
A else
menudo puede existir un bloque para complementar la funcionalidad que ocurre en cada except
bloque.
try:
test_consistency(valuable_data)
except Except1:
inconsistency_type = 1
except Except2:
inconsistency_type = 2
except:
# Something else is wrong
raise
else:
inconsistency_type = 0
"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""
En este caso, inconsistency_type
se establece en cada bloque excepto, de modo que el comportamiento se complementa en el caso sin error en else
.
Por supuesto, estoy describiendo esto como un patrón que puede aparecer en su propio código algún día. En este caso específico, de todos modos solo establece inconsistency_type
0 antes del try
bloque.
Aquí hay otro lugar donde me gusta usar este patrón:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
continue
lugar, puede usar el patrón "salir temprano". Esto le permite eliminar la cláusula "else" y su sangría, haciendo que el código sea más fácil de leer.
Uno de los escenarios de uso que se me ocurren son las excepciones impredecibles, que se pueden eludir si lo intentas nuevamente. Por ejemplo, cuando las operaciones en el bloque try implican números aleatorios:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
Pero si se puede predecir la excepción, siempre debe elegir la validación de antemano sobre una excepción. Sin embargo, no todo se puede predecir, por lo que este patrón de código tiene su lugar.
break
interior try
al final, que es IMO más limpio, y no necesita el else
. Además, continue
no es realmente necesario, puede simplemente pass
.
He encontrado else
útil para tratar con un archivo de configuración posiblemente incorrecto:
try:
value, unit = cfg['lock'].split()
except ValueError:
msg = 'lock monitoring config must consist of two words separated by white space'
self.log('warn', msg)
else:
# get on with lock monitoring if config is ok
Una excepción al leer la lock
configuración deshabilita la supervisión de bloqueo y ValueErrors registra un mensaje de advertencia útil.
Suponga que su lógica de programación depende de si un diccionario tiene una entrada con una clave determinada. Puede probar el resultado del dict.get(key)
uso de la if... else...
construcción, o puede hacer:
try:
val = dic[key]
except KeyError:
do_some_stuff()
else:
do_some_stuff_with_val(val)
Agregaría otro caso de uso que parece sencillo al manejar sesiones de DB:
# getting a DB connection
conn = db.engine.connect()
# and binding to a DB session
session = db.get_session(bind=conn)
try:
# we build the query to DB
q = session.query(MyTable).filter(MyTable.col1 == 'query_val')
# i.e retrieve one row
data_set = q.one_or_none()
# return results
return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]
except:
# here we make sure to rollback the transaction,
# handy when we update stuff into DB
session.rollback()
raise
else:
# when no errors then we can commit DB changes
session.commit()
finally:
# and finally we can close the session
session.close()
El else:
bloque es confuso y (casi) inútil. También es parte de las declaraciones for
y while
.
En realidad, incluso en una if
declaración, else:
se puede abusar de formas realmente terribles creando errores que son muy difíciles de encontrar.
Considera esto.
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
else:
# Exactly what is true here?
# Can be hard to reason out what condition is true
Piénsalo dos veces else:
. Generalmente es un problema. if
Evítelo excepto en una declaración e incluso considere documentar la else
condición - para hacerlo explícito.
if x > 0: return "yes"
y if x <= 0: return "no"
. Ahora viene una persona y cambia una de las condiciones para decir, x > 1
pero se olvida de cambiar la otra. ¿Cómo es esa reducción de la cantidad de errores que se cometerían? if else
a veces las cláusulas están separadas por muchas líneas. DRY es una buena práctica, la mayoría de las veces, en realidad. (Lo siento por el doble post).