Purpose of else and finally in exception handling

Python

Python Problem Overview


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?

Python Solutions


Solution 1 - Python

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.

Solution 2 - Python

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.

Solution 3 - Python

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.

Solution 4 - Python

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!")

Solution 5 - Python

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()

Solution 6 - Python

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 > 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 > 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjhourbackView Question on Stackoverflow
Solution 1 - PythonBrian FisherView Answer on Stackoverflow
Solution 2 - PythonorlpView Answer on Stackoverflow
Solution 3 - PythonSven MarnachView Answer on Stackoverflow
Solution 4 - PythonNasir ShahView Answer on Stackoverflow
Solution 5 - PythonReza JenabiView Answer on Stackoverflow
Solution 6 - PythonmorkView Answer on Stackoverflow