I was going through the python docs to improve my core python and I was reading about errors and exceptions
In the doc it says
If a finally clause includes a return statement, the finally clause’s return statement will execute before, and instead of, the return statement in a try clause.
It also provides this example below:
def bool_return():
try:
return True
finally:
return False
bool_return()
Now looking at this example the above statement seems straight and fair enough, but if you modify that example a bit to make it look like this:
def bool_return():
try:
return print("foo")
finally:
return False
bool_return()
Now, if you run this you will see that foo
will be printed and False will be returned. Now the doc says that finally clause's return will execute before, and instead of, try clause's return statement. If so, then why I can see the foo being printed?
I debugged this snippet with pycharm and it shows that the try clause's return statement is executed first and the string is printed and then it's output which is None
is returned due to return
statement, and the return statement in the finally clause will be executed later, which is the last return of the program so the function overrides previous return and False
is returned.
My question is:
1) Why does doc say finally clause's return statement is executed before?
2) Why does doc say finally clause's return statement is executed instead of try clause's return statement?
I believe both the statements are the opposite of what happens in reality.
EDIT:
After reading @iBug's answer it is clear now that how the print("foo")
is evaluated but None
is not returned. Basically, the expression is evaluated first and then return
happens. Later on return False
in finally is executed. Which makes clear why we get the output that we did.
Still, I see that the return False
in finally is executed after the return print("foo")
of try.
Or as per @iBug's comment, 10 RETURN_VALUE
is completely bypassed?
EDIT
This is now resolved in the documentation and it is correct now on what will be returned. However, if you wish to know "how" then read all the comments and answer carefully.
$ python3
Python 3.7.5 (default, Nov 20 2019, 09:21:52)
[GCC 9.2.1 20191008] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def bool_return():
... try:
... return print("foo")
... finally:
... return False
...
>>> import dis
>>> dis.dis(bool_return)
2 0 SETUP_FINALLY 8 (to 10)
3 2 LOAD_GLOBAL 0 (print)
4 LOAD_CONST 1 ('foo')
6 CALL_FUNCTION 1
8 RETURN_VALUE
5 >> 10 LOAD_CONST 2 (False)
12 RETURN_VALUE
>>>
As you can see above, return False
does happen before the return
statement in the try
block, but after the to-be-returned value has been computed.
I think the docs probably meant "the very action of returning" by return statement, or in other words, it didn't take into account the computation of the return value, which of course happens before it's returned.
To observe whether 8 RETURN_VALUE
is executed or not, you can compile CPython interpreter in debug mode and run it in GDB. A step-by-step guide would be too bloated for this answer, so I'll give an outline here (Linux).
./configure --with-pydebug
(you may want to give --prefix=/opt/python3-debug
as well), make
and make install
gdb /opt/python3-debug/bin/python3
and (gdb) r
bool_return
as usual.RETURN_VALUE
in Python/ceval.c
, take down the line number (for 3.8.1, it's 1911).SIGTRAP
, and set a breakpoint at the position from the previous step (b Python/ceval.c:1911
), and then c
.(gdb breakpoint info)
False
(gdb breakpoint info)
return
statement in the function.Now that it's clear that only one return
has been executed in the function, it must be 12 RETURN_VALUE
, so the Python instruction 8 RETURN_VALUE
isn't executed at all.