Python Try Except: An Illustrated Guide

5/5 - (2 votes)

What is Try/Except in Python?

Python’s try and except keywords are used to ‘catch’ an exception and handle it, preventing it from terminating your program as it normally would.

This could be useful in situations where you know there’s potential for an exception to occur, and you want your program to be able to continue even if it happens.

What is an Exception?

An exception refers to an ‘exceptional’ or rare situation or condition during the execution of a program. You’ll probably be familiar with many of the common Python built-in exceptions such as IndexError, ValueError, and TypeError.

There are many more, and you can read about them in the python docs as well as how to create your own exception classes.

The process of attempting to detect (catch) and deal with (handle) exceptions is known as exception handling.

Basic Syntax

Minimal Example

In this Python try-except example, we want to convert an inputted value into an integer, but we use the try-except block because we know there’s a possibility that the number was written as a word rather than as a number.

You can see that the # next part of the program is executed even if an exception is raised.

Input:

input_number = 'five'

try:
    input_number = int(input_number)
except ValueError as e:
    print(f'Error: {e}')

# Next part of the program
print("I'm next")

Output:

Error: invalid literal for int() with base 10: 'five'
I'm next

As expected, we caught and printed the ValueError, and the next part of the program also ran. 

Python Try/Except … Else and Finally

There are two more methods to cover in the try statement and they are else and finally.

  • The else clause will execute if no exception is raised in the try clause.
  • The finally clause will execute regardless of whether an exception is raised or not. This is often used for ‘cleaning up’ actions such as closing a file you had opened in the try clause. 

Let’s jump straight into an example to see how they work.

Input: 

nums = [3, 0.4, 8, 0]

for num in nums:
    try:
        new_val = 1 / int(num)
    except ZeroDivisionError as e:
        print(f'Error: {e}')
    else:
        print(f'New Value: {new_val}')
    finally:
        print("Another one bites the dust")

# Next part of the program
print("I'm next")

Output:

New Value: 0.3333333333333333
Another one bites the dust
Error: division by zero
Another one bites the dust
New Value: 0.125
Another one bites the dust
Error: division by zero
Another one bites the dust
I'm next

A Complete Visual Overview of Try/Except

The order of events in execution of a Python try-except block (try statement) is:

  1. The try clause is executed
  2. If there’s no exception during step 1;
    1. The except clause/s are skipped
    2. The else block executes if there is one
    3. The finally block executes if there is one
    4. The try statement is now complete
    5. The program after the try statement is continued
  3. If an exception occurs during the execution of the try clause;
    1. The rest of the try clause is skipped
    2. If the type of the exception that occurred matches (one of) the type(s) after the except keyword(s);
      1. The except clause is executed
      2. The finally block executes if there is one
      3. The try statement is now complete
      4. The program after the try statement is continued
    3. If the type of the exception does not match one of the types after except keywords;
      1. It is passed to any outer try statements, if no handler is found, it is an unhandled exception and execution of the program stops

You can see this visualized in the following flow diagram:

Does the “try” Statement Look Ahead?

Note that the try statement does not ‘look ahead’ at the try clause and then only execute if no error is found. The code in the try clause executes as normal, and then it is only stopped at the point that an exception is found.

For example, in the code below, the list items are still printed out until the IndexError exception is reached at i = 3.

Input:

lst = ['a', 'b', 'c']

try:

    for i in range(10):
        print(lst[i])

except Exception as e:
    print(f'Error: {e}')

Output:

a
b
c
Error: list index out of range

Exception Names and Hierarchies

As you can see in the example above, you don’t need to name the specific exception in order to make the try statement work. You can even leave out the name altogether and just write ‘except:’ followed by your handler but that is considered bad practice (in most cases).

PEP8 recommends “Mention specific exceptions whenever possible instead of bare ‘except’ clause.”

You can refer to the built-in exception hierarchy in the Python docs and see that there are ‘parent nodes’ in the hierarchy which will catch any exception type in a child relation with it. So using ‘Exception’ like I used in the example above will catch any exception excluding:

  • SystemExit
  • KeyboardInterrupt
  • GeneratorExit

BaseException’ is at the top of the hierarchy and will match any exception type which makes it equivalent to a bare ‘except:’ and therefore should also be avoided in most cases.

Multiple Exception Names and Except Clauses

You can use multiple except clauses to handle different exception types differently and/or group exception types together in the same except clause.

Here’s an example:

Input:

import random


lst = [4, 'six', '8', 'cat', [], 0]
idx = random.randint(0, 7)
total = 1

try:
    itm = lst[idx]
    print(f'List item = {itm}')
    den = int(itm)
    print(total/den)
except IndexError as e:
    print(f'My Index Error: {e}')
except TypeError as e:
    print(f'My Type Error: {e}')
except (ValueError, ZeroDivisionError) as e:
    print(f'My Value or Zero Division Error: {e}')

Output: 

The output will change based on the specific exception type in each case. If you run the code multiple times you can see the different errors being caught by the different except clauses.

Should You Avoid try-except in Python?

You only need to perform a cursory search of coding forums to notice that there seems to be a lot of controversy amongst developers over whether it’s considered good practice to use try-except in Python.

However, many of the arguments against using try-except (aka try-catch) are rooted in the cultures and best practices of programming languages other than Python so they need to be taken with a grain of salt. 

The question of whether it’s bad and when it’s appropriate remains so let’s explore that a little bit here. 

When is it bad to use try-catch?

The arguments against try-except (try-catch) mostly relate to these three concerns:

  • Danger of accidentally catching an error you didn’t realise you were catching and therefore masking that error and making it difficult to find and debug
  • Speed issues: while a try statement is fast if no exceptions occur, an exception being thrown is slower than an alternative approach such as lots of if/then statements
  • May encourage sloppy/lazy programming 

Accidentally masking another error is a legitimate concern. Take the following code snippet for example:

Input:

lst = [['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'e']]

try:
    for i in range(len(lst)+1):
        # May produce IndexError
        for j in range(3):
            print(lst[i][j])

except IndexError as e:
    print(f'Error: {e}')

Output:

a
b
c
b
c
d
c
d
e
Error: list index out of range

You’ve anticipated a potential IndexError in the inner for loop but in doing so you’ve masked an error in the outer for loop which is throwing the exception in this case.

The error being that the code range(len(lst)+1) will always lead to the program trying to access an index of the list which is outside the list: in this case index ‘3’ for a list where the final element is at index ‘2’.

PEP8 has some simple words of wisdom to help us with this: “Limit the try clause to the minimum amount of code necessary to avoid masking bugs.”

If the program you’re writing is mission-critical and an error like this could have very bad consequences then maybe avoiding try/except is a good idea.

  • On the other hand, try except is an accepted part of the language and an example of the pythonic EAFP coding practice which stands for ‘Easier to Ask for Forgiveness than Permission.’
  • This can be contrasted with LBYL which stands for ‘Look Before You Leap’ which is a more common practice in other programming languages. 

In Python, try/except has many legitimate use cases, including;

  1. If you feel an exception is unlikely and you don’t think it’s worth writing a LBYL check or checks for it
  2. It makes your code simpler and more readable 
  3. It makes your code faster/more efficient

Here’s a decision tree to help you: