From the Python documentation
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in a except or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement. A more complicated example (having except and finally clauses in the same try statement works as of Python 2.5):
So once the try/except block is left using return, which would set the return value to given - finally blocks will always execute, and should be used to free resources etc. while using there another return - overwrites the original one.
In your particular case, func1() returns 2 and func2() returns 3, as these are values returned in the finally blocks.
From the Python documentation
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in a except or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement. A more complicated example (having except and finally clauses in the same try statement works as of Python 2.5):
So once the try/except block is left using return, which would set the return value to given - finally blocks will always execute, and should be used to free resources etc. while using there another return - overwrites the original one.
In your particular case, func1() returns 2 and func2() returns 3, as these are values returned in the finally blocks.
It will always go to the finally block, so it will ignore the return in the try and except. If you would have a return above the try and except, it would return that value.
def func1():
try:
return 1 # ignoring the return
finally:
return 2 # returns this return
def func2():
try:
raise ValueError()
except:
# is going to this exception block, but ignores the return because it needs to go to the finally
return 1
finally:
return 3
def func3():
return 0 # finds a return here, before the try except and finally block, so it will use this return
try:
raise ValueError()
except:
return 1
finally:
return 3
func1() # returns 2
func2() # returns 3
func3() # returns 0
Run code finally before returning in except
python - Weird Try-Except-Else-Finally behavior with Return statements - Stack Overflow
Python function with try, except, finally, and return
Python confusion with return value in try-except-finally - Stack Overflow
Videos
Ok, that was an awful title and I'm sorry... but it's hard to phrase! My question overall is, if we use a try statement in a function (and let's imagine it works, we don't end up having to handle any exceptions) and there is a return statement in this try-block. Won't that cause us to leave the function? Since, we are returning control back to main, let's say.
But, we have a finally statement in our function to. It might do something trivial like print something. Does this get executed even though we should have hit return?
Now, I have tested this. And what it seems to do is reach the return statement, ignore it, carry out the finally statement and then go back to the return. But I would like to know if I am understanding this correctly.
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.
The execution order is:
- try block all completes normally -> finally block -> function ends
- try block run and get into exception A -> finally block -> function ends
- 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.
You're running into the difference between an identifier and a value. num += 1 is creating a new int object and assigning the num identifier to point to it. It does not change the int object the identifier is already pointing to. (For small values the int objects are cached but that's an implementation detail)
You can see the difference with an operation that does mutate the object in the below code:
def y():
l = []
try:
raise Exception
except Exception:
print("except")
l.append(1)
return l
finally:
print("finally")
l.append(2)
print(y())
# except
# finally
# [1, 2]
The finally is executed (this is clearly defined in the documentation), but as you return an immutable object, the modification is unseen as your returned name is now part of a different scope.
This would work as you expect with a mutable object, for instance a list:
def main():
lst = [0]
try:
raise Exception('This is the error message.')
except Exception:
lst[0] += 1
return lst
finally:
lst[0] += 1
a = main()
print(a)
Output: [2]
It makes a difference if you return early:
try:
run_code1()
except TypeError:
run_code2()
return None # The finally block is run before the method returns
finally:
other_code()
Compare to this:
try:
run_code1()
except TypeError:
run_code2()
return None
other_code() # This doesn't get run if there's an exception.
Other situations that can cause differences:
- If an exception is thrown inside the except block.
- If an exception is thrown in
run_code1()but it's not aTypeError. - Other control flow statements such as
continueandbreakstatements.
You can use finally to make sure files or resources are closed or released regardless of whether an exception occurs, even if you don't catch the exception. (Or if you don't catch that specific exception.)
myfile = open("test.txt", "w")
try:
myfile.write("the Answer is: ")
myfile.write(42) # raises TypeError, which will be propagated to caller
finally:
myfile.close() # will be executed before TypeError is propagated
In this example you'd be better off using the with statement, but this kind of structure can be used for other kinds of resources.
A few years later, I wrote a blog post about an abuse of finally that readers may find amusing.