Python try-else

582

¿Cuál es el uso previsto de la elsecláusula opcional de la trydeclaración?

geowa4
fuente
1
La mayoría de las respuestas parecen concentrarse en por qué no podemos simplemente poner el material en la cláusula else en la cláusula try misma. La pregunta stackoverflow.com/questions/3996329 pregunta específicamente por qué el código de la cláusula else no puede ir después del bloque try, y esa pregunta está duplicada a esta, pero no veo una respuesta clara a esa pregunta aquí. Siento que stackoverflow.com/a/3996378/1503120 responde excelentemente esa pregunta. También he tratado de dilucidar la importancia de las diversas cláusulas en stackoverflow.com/a/22579805/1503120 .
jamadagni
Desea que ocurra algo si la excepción no se dispara, antes de la limpieza final, que nunca se supone que desencadene el mismo manejo de excepciones.
benjimin

Respuestas:

858

Las declaraciones en el elsebloque 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, exceptdetectaría los errores de la segunda llamada. Y si lo coloca después de todo el trybloque, siempre se ejecutará, y no hasta después del finally. El elsete permite asegurarte

  1. la segunda operación solo se ejecuta si no hay excepción,
  2. se ejecuta antes del finallybloque, y
  3. cualquier IOErrors que surja no se ve atrapado aquí
Blair Conrad
fuente
77
También tenga en cuenta que las variables utilizadas en el bloque try se PUEDEN usar en el bloque else, por lo que siempre debe considerar usar esta variante si no espera más excepciones en el bloque
else
3
Eso no importa, porque las variables de ámbito de prueba se ven fuera de la prueba, ya sea que haya otra cosa o no.
Reinderien
36
No existe una "variable de alcance de prueba". En Python, los ámbitos variables se establecen solo por módulos, funciones y comprensiones, no por estructuras de control.
mhsmith
99
La cláusula else le permite escribir código que solo tiene sentido si no se produjo una excepción; la cláusula except simplemente puede pasar. Si coloca la lógica en el bloque de prueba, corre el riesgo de ocultar silenciosamente los errores en su código. Nunca aplastar excepciones que no esperaba.
Alice Purcell
99
de esta respuesta no está claro qué significa "caerse del fondo" : esto no solo sucede debido a una excepción, sino también a causa de un return, continueo break.
Antti Haapala
108

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 exceptno 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".

Izkata
fuente
53

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).

Darius Bacon
fuente
50

Python try-else

¿Cuál es el uso previsto de la elseclá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, continuey breakpuede interrumpir el flujo de control a else.

En resumen

La elseinstrucción se ejecuta si hay no hay excepciones y si no es interrumpida por una return, continueo breakcomunicado.

Las otras respuestas pierden esa última parte.

De los documentos:

La elsecláusula opcional se ejecuta siempre y cuando el control fluya fuera del final de la tryclá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, continueo breakcomunicado.

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.

Ejemplo de diferenciación elsefrente al código que sigue al trybloque

Si maneja un error, el elsebloque 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!
Aaron Hall
fuente
26

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.

Alice Purcell
fuente
19

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()
RoadieRich
fuente
9

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 somethingdefinida si no se produce ningún error. Puede eliminar esto fuera del trybloque, pero luego requiere una detección desordenada si se define una variable.

Desconocido
fuente
3
¿Qué hay de malo something = a[2]; print somethingen el intento: bloquear?
S.Lott
@ S.Lott nada, pero ¿qué pasa si alguien le envía una lista y no desea mostrar los datos si no es lo suficientemente largo porque probablemente esté dañado?
Desconocido
12
S. Lott: 'imprimir algo' podría generar una excepción diferente que no desea interceptar.
Darius Bacon
No veo la diferencia Si obtengo una excepción fuera de límites, imprime "fuera de límites". Lo tengo. Si obtengo alguna otra excepción, este bloque de código no lo detecta. Si no obtengo ninguna excepción, el comportamiento es imprimir el valor de algo, que es un [2]. No veo qué más hace en este ejemplo.
S.Lott
3
El valor de 'algo', cuando se imprime, puede generar el error en su método __str __ (). Si bien ese valor es en realidad solo 2 en este ejemplo, también podría señalar que tampoco hay una excepción fuera de los límites aquí.
Darius Bacon
8

Hay un buen ejemplo de try-elseen 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.

itsadok
fuente
7

De errores y excepciones # Excepciones de manejo - docs.python.org

La try ... exceptdeclaración tiene una elseclá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.

fedorqui 'así que deja de dañar'
fuente
6

Mirando la referencia de Python , parece que elsese ejecuta después trycuando 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 trybloque 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 elsebloquear y vincular lo que se requiere (ver enlace para el ejemplo y explicación).

Si intentaste trabajar en catchbloque, podría arrojar otra excepción: supongo que ahí es donde el elsebloque resulta útil.

stefanB
fuente
44
"Las excepciones en la cláusula else no son manejadas por las cláusulas anteriores, excepto". Esa es la parte útil. Gracias.
geowa4
"La cláusula else opcional se ejecuta si y cuando el control fluye fuera del final de la cláusula try" es otra diferencia, ya que puede volver fuera del trybloque.
Tomer W
4

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.

tzot
fuente
2

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.

DevPlayer
fuente
2

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.

sirlark
fuente
1

A elsemenudo puede existir un bloque para complementar la funcionalidad que ocurre en cada exceptbloque.

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_typese 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_type0 antes del trybloque.

Wesley
fuente
1

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`
grdvnl
fuente
1
En su continuelugar, 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.
Malté
1

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.

NeoWang
fuente
1
Puede hacer esto colocando el breakinterior tryal final, que es IMO más limpio, y no necesita el else. Además, continueno es realmente necesario, puede simplemente pass.
Dirbaio
1

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 lockconfiguración deshabilita la supervisión de bloqueo y ValueErrors registra un mensaje de advertencia útil.

Mella
fuente
1

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)
Gene
fuente
-1

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()
vadimbog
fuente
-17

El else:bloque es confuso y (casi) inútil. También es parte de las declaraciones fory while.

En realidad, incluso en una ifdeclaració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. ifEvítelo excepto en una declaración e incluso considere documentar la elsecondición - para hacerlo explícito.

S.Lott
fuente
66
No estaría de acuerdo con este. En el bloque "if-elif", "else" se usa como "predeterminado" se usaría en el bloque "case" del lenguaje C. Siempre se recomienda manejar el caso "predeterminado" incluso si cree que ha cubierto todos los casos en diversas condiciones.
Josip
1
@Josip: usado como "predeterminado" puede ser confuso. El problema es definir claramente la condición que es este "defecto". Una condición predeterminada mal definida puede ser la causa raíz del comportamiento defectuoso. Otra cosa puede ser causa de confusión. Debe pensarse con mucho cuidado en todos los casos, no solo intentar, por un tiempo, sino también.
S.Lott
55
Bueno, el código anterior es totalmente abstracto y no hace nada significativo, así que sí, no es de extrañar que sea confuso.
julx
1
@ S.Lott "Reduciría el error", y mi punto es que esto es falso. Creo que solo tenemos una diferencia genuina en las opiniones. Los malos programadores siempre encuentran formas de escribir programas con errores. Siempre. Los buenos programadores siempre buscan buenas prácticas y pueden escribir un buen código en casi cualquier idioma. Eliminar las construcciones útiles solo da menos poder a los buenos programadores sin ayudar particularmente a los malos, ya que son capaces de inventar un número infinito de formas de joder las cosas.
julx
55
Considere: if x > 0: return "yes"y if x <= 0: return "no". Ahora viene una persona y cambia una de las condiciones para decir, x > 1pero se olvida de cambiar la otra. ¿Cómo es esa reducción de la cantidad de errores que se cometerían? if elsea 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).
julx