Comportamiento extraño de Try-Except-Else-Finalmente con declaraciones de retorno

88

Este es un código que se comporta de manera peculiar. Esta es una versión simplificada del comportamiento que he escrito. Esto todavía demostrará el comportamiento extraño y tenía algunas preguntas específicas sobre por qué ocurre esto.

Estoy usando Python 2.6.6 en Windows 7.

def demo1():
    try:
        raise RuntimeError,"To Force Issue"
    except:
        return 1
    else:
        return 2
    finally:
        return 3

def demo2():
    try:
        try:
            raise RuntimeError,"To Force Issue"
        except:
            return 1
        else:
            return 2
        finally:
            return 3
    except:
        print 4
    else:
        print 5
    finally:
        print 6

Resultados:

>>> print demo1()
3
>>> print demo2()
6
3
  • ¿Por qué la demostración uno devuelve 3 en lugar de 1?
  • ¿Por qué la demostración dos imprime 6 en lugar de imprimir 6 con 4 o 5?
Kyle Owens
fuente

Respuestas:

124

Porque finallyse garantiza que las declaraciones se ejecutarán (bueno, suponiendo que no haya cortes de energía ni nada fuera del control de Python). Esto significa que antes de que la función pueda regresar, debe ejecutar el bloque finalmente, que devuelve un valor diferente.

Los documentos de Python dicen:

Cuando se ejecuta una instrucción return, break o continue en la suite try de una instrucción try ... finalmente, la cláusula finalmente también se ejecuta 'al salir'.

El valor de retorno de una función está determinado por la última instrucción de retorno ejecutada. Dado que la cláusula final siempre se ejecuta, una declaración de retorno ejecutada en la cláusula final siempre será la última ejecutada:

Esto significa que cuando intenta regresar, finallyse llama al bloque, devolviendo su valor, en lugar del que hubiera tenido.

Gareth Latty
fuente
4
¿Por qué no se imprime el 5 en el segundo ejemplo? esto todavía no está bien explicado, creo. la devolución está bien respondida, pero ¿por qué no se imprime el 5 en el segundo ejemplo
Joran Beasley
5
oh, creo que me di cuenta de que el retorno en el intento inicial hace que salte inmediatamente al exterior finalmente
Joran Beasley
2
Exactamente, porque los finallybloques siempre se ejecutan.
Gareth Latty
2
En la demostración dos, ¿por qué ejecuta el anidado finalmente, patear al exterior finalmente y luego volver al anidado finalmente para terminar el retorno en lugar de simplemente devolver Ninguno desde el exterior finalmente?
Kyle Owens
1
Porque cuando returnse llama a la declaración, Python comprueba si hay finallycláusulas abiertas que deban ejecutarse (consulte la cita anterior).
Gareth Latty
6

La orden de ejecución es:

  1. intentar bloquear todo se completa normalmente -> finalmente bloquear -> finaliza la función
  2. intente ejecutar el bloque y entrar en la excepción A -> finalmente bloquear -> finaliza la función
  3. intente bloquear hacer un valor de retorno y llamar al retorno -> finalmente bloquear -> valor de retorno emergente -> finaliza la función

Por lo tanto, cualquier devolución en el bloque finalmente finalizará los pasos por adelantado.

Fred Huang
fuente