118

This is some code that is behaving peculiarly. This is a simplified version of the behavior that I've written. This will still demonstrate the weird behavior and I had some specific questions on why this is occurring.

I'm using Python 2.6.6 on 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

Results:

>>> print demo1()
3
>>> print demo2()
6
3
  • Why is demo one returning 3 instead of 1?
  • Why is demo two printing 6 instead of printing 6 w/ 4 or 5?
wim
  • 338,267
  • 99
  • 616
  • 750
Kyle Owens
  • 1,321
  • 2
  • 9
  • 10

3 Answers3

150

Because finally statements are guaranteed to be executed (well, presuming no power outage or anything outside of Python's control). This means that before the function can return, it must run the finally block, which returns a different value.

The Python docs state:

When a return, break or continue statement is executed in the try suite of a try…finally statement, the finally clause is also executed ‘on the way out.’

The return value of a function is determined by the last return statement executed. Since the finally clause always executes, a return statement executed in the finally clause will always be the last one executed:

This means that when you try to return, the finally block is called, returning it's value, rather than the one that you would have had.

Community
  • 1
  • 1
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 6
    why doesnt the 5 print in the second example? this is still not well explained I think. the return one is well answered but why doesnt the 5 in the second example print – Joran Beasley Jun 22 '12 at 21:14
  • 5
    oh i think i figured it out the return in the initial try causes it to jump immediately to the outer finally – Joran Beasley Jun 22 '12 at 21:19
  • 2
    Exactly, because `finally` blocks **always** run. – Gareth Latty Jun 22 '12 at 21:20
  • 2
    In demo two, why does it execute the nested finally, kick out to the outside finally then go back into the nested finally to finish the return instead of simply returning None from the outside finally? – Kyle Owens Jun 22 '12 at 21:23
  • 2
    Because when the `return` statement is called, Python checks for any open `finally` clauses that need to be executed (see the quote above). – Gareth Latty Jun 22 '12 at 21:28
  • This is really bad behavior, because in fact method making 2 return point. So it basically silently rewrite return stack. – Andrey Nikishaev Oct 30 '13 at 11:42
  • 1
    Part of the reason this seems more confusing is because of the fact that `print 6` executes AFTER `return 3`, but the `3` doesn't get printed until after `print 6` is executed because it's printed in the parent function. With that insight, the behavior is fairly obvious (inner finally runs, then outer finally.) – John Chadwick Jun 17 '15 at 17:13
  • Same problem here when raising exceptions on the except and then executing finally! I will NOT raise the exception, and instead return the value in finally! – juan Isaza Sep 27 '16 at 16:46
  • I was even more confused because I read `print 6` as `return 6`, and the fact that they both got printed must mean they both got returned and then... `print demo2()` got executed twice? "How can a python function return **two** values?" – KevinOrr Oct 13 '16 at 20:45
  • @KevinOrr A python function actually **can** return two (or more) values, but this isn't the case there. As you said, you were confused by reading `print 6` as `return 6`. Here instead there's a `print 6` that's executed **after** the `return 3`, and then the calling function prints the returned value 3. This behavior is exactly as it should be. – Bozzy Nov 16 '16 at 16:06
  • @bozzy It is a bit misleading to say a function can return multiple values. It can return a tuple, but only at one time. You can't `return` twice. – Gareth Latty Nov 16 '16 at 16:09
  • Yes, I was too simplistic because the subject was OT. – Bozzy Nov 16 '16 at 16:16
9

The execution order is:

  1. try block all completes normally -> finally block -> function ends
  2. try block run and get into exception A -> finally block -> function ends
  3. try block make a return value and call return -> finally block -> popup return value -> function ends

So, any return in the finally block will end the steps in advance.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Fred Huang
  • 129
  • 1
  • 2
3

Note that there has been PEP601 to forbid return statements from finally clauses, but it has been rejected. It has however been added to the style guide in PEP8 that it should be avoided.

yanlend
  • 450
  • 3
  • 10