Dziwne zachowanie Try-Except-Else-Final z instrukcjami Return

88

To jest kod, który zachowuje się dziwnie. To jest uproszczona wersja zachowania, które napisałem. To nadal pokaże dziwne zachowanie i miałem kilka konkretnych pytań, dlaczego tak się dzieje.

Używam Pythona 2.6.6 w systemie 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

Wyniki:

>>> print demo1()
3
>>> print demo2()
6
3
  • Dlaczego wersja demo 1 zwraca 3 zamiast 1?
  • Dlaczego wersja demonstracyjna 2 drukuje 6 zamiast drukowania 6 w / 4 lub 5?
Kyle Owens
źródło

Odpowiedzi:

124

Ponieważ finallyoświadczenia są gwarantowane do wykonania (dobrze, zakładając żadnej przerwy w dostawie prądu lub cokolwiek poza kontrolą Pythona). Oznacza to, że zanim funkcja będzie mogła powrócić, musi uruchomić ostatni blok, który zwraca inną wartość.

Stan dokumentacji Pythona :

Kiedy instrukcja return, break lub continue jest wykonywana w zestawie try instrukcji try… final, klauzula last jest również wykonywana „w drodze”.

Wartość zwracana funkcji jest określana przez ostatnią wykonaną instrukcję return. Ponieważ klauzula Final jest zawsze wykonywana, instrukcja return wykonywana w klauzuli Final zawsze będzie ostatnią wykonaną:

Oznacza to, że kiedy próbujesz zwrócić, finallywywoływany jest blok, zwracając jego wartość, a nie tę, którą byś miał.

Gareth Latty
źródło
4
dlaczego 5 nie jest drukowane w drugim przykładzie? myślę, że to wciąż nie jest dobrze wyjaśnione. odpowiedź zwrotna jest dobra, ale dlaczego 5 w drugim przykładzie nie jest drukowane
Joran Beasley
5
och, myślę, że to rozgryzłem, powrót w początkowej próbie powoduje natychmiastowy przeskok do zewnętrznej w końcu
Joran Beasley
2
Dokładnie, ponieważ finallybloki zawsze biegną.
Gareth Latty
2
W wersji demonstracyjnej 2, dlaczego w końcu wykonuje zagnieżdżenie, w końcu wyrzuca na zewnątrz, a następnie wraca do zagnieżdżonego w końcu, aby zakończyć powrót, zamiast po prostu w końcu zwrócić None z zewnątrz?
Kyle Owens
1
Ponieważ po returnwywołaniu instrukcji Python sprawdza, czy nie ma otwartych finallyklauzul, które muszą zostać wykonane (zobacz cytat powyżej).
Gareth Latty
6

Kolejność wykonania to:

  1. try block all kończy się normalnie -> w końcu blok -> funkcja kończy się
  2. spróbuj wykonać blok i przejdź do wyjątku A -> w końcu blok -> funkcja się kończy
  3. try block zwróć wartość i wywołaj return -> w końcu block -> popup wartość zwracana -> funkcja kończy się

Tak więc każdy powrót w ostatnim bloku kończy kroki z góry.

Fred Huang
źródło