Purpose of else and finally in exception handling

Are the else and finally sections of exception handling redundant? For example, is there any difference between the following two code snippets?

try:
foo = open("foo.txt")
except IOError:
print("error")
else:
print(foo.read())
finally:
print("finished")

and

try:
foo = open("foo.txt")
print(foo.read())
except IOError:
print("error")
print("finished")

More generally, can't the contents of else always be moved into the try, and can't the contents of finally just be moved outside the try/catch block? If so, what is the purpose of else and finally? Is it just to enhance readability?

22201 次浏览

finally is executed regardless of whether the statements in the try block fail or succeed. else is executed only if the statements in the try block don't raise an exception.

No matter what happens, the block in the finally always gets executed. Even if an exception wasn't handled or the exception handlers themselves generate new exceptions.

If you move the contents of the else block inside the try block, you will also catch exceptions that might happen during the else block. If the line

print(foo.read())

in your example throws an IOError, your first code snippet won't catch that error, while your second snippet will. You try to keep try blocks as small as possible generally to really only catch the exceptions you want to catch.

The finally block gets always executed, no matter what. If for example the try block contains a return statement, a finally block will still be executed, while any code beneath the whole try/except block won't.

try:
print("I may raise an exception!")
except:
print("I will be called only if exception occur!")
else:
print("I will be called only if exception didn't occur!")
finally:
print("I will be called always!")

finally block always executed

Else block executed If there is not any exception.

I prefer to put the code in finally block which is always executed after try and except blocks.

I prefer to put the code in else block which is executed if the try clause does not raise an exception same like this

Finally

try:
f = open("file.txt")
f.write("change file")
except:
print("wrong")
finally:
f.close()

Else

try:
f = open("file.txt")
f.write("change file")
except:
print("wrong")
else:
print("log => there is not any exception")
finally:
f.close()

There are 3 possible "states": never occurred, handled and unhandled. You can map the control flow of the try-catch-else-finally clause into these 3 states like that:

from traceback import print_last


e_state = 'unhandled exception'
try:
# cause an exception here [or don't]
except SomeException as e: # use a suitable [or not] exception type here
e_state = 'handled exception'
print('in "except"')
else:
e_state = 'no exception'
print('in "else"')
finally:
print(f'in "finally". {e_state} occurred')
if e_state == 'handled exception':
print_last() # since the exception was caught - explicitly inform about it

Full examples below:

1. Handled Exception

from traceback import print_last


e_state = 'unhandled exception'
try:
1 / 0
except ZeroDivisionError as e:
e_state = 'handled exception'
print('in "except"')
else:
e_state = 'no exception'
print('in "else"')
finally:
print(f'in "finally". {e_state} occurred')
if e_state == 'handled exception':
print_last()

Output:

in "except"
in "finally". handled exception occurred


Traceback (most recent call last):
File "...IPython/core/interactiveshell.py", line 3251, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File ".../T/ipykernel_59815/1316012763.py", line 5, in <module>
1 / 0
ZeroDivisionError: division by zero

2. Unhandled Exception

from traceback import print_last


e_state = 'unhandled exception'
try:
1 / 0
except KeyError as e:
e_state = 'handled exception'
print('in "except"')
else:
e_state = 'no exception'
print('in "else"')
finally:
print(f'in "finally". {e_state} occurred')
if e_state == 'handled exception':
print_last()

Output:

in "finally". unhandled exception occurred


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Input In [14], in <module>
3 e_state = 'unhandled exception'
4 try:
----> 5     1 / 0
6 except KeyError as e:
7     e_state = 'handled exception'


ZeroDivisionError: division by zero

3. No Exception

from traceback import print_last


e_state = 'unhandled exception'
try:
1 / 2
except ZeroDivisionError as e:
e_state = 'handled exception'
print('in "except"')
else:
e_state = 'no exception'
print('in "else"')
finally:
print(f'in "finally". {e_state} occurred')
if e_state == 'handled exception':
print_last()

Output:

in "else"
in "finally". no exception occurred