Try/except#

This page covers the try/except construct in the python programming languages - its typical syntax and patterns that are related to them.

Re-rising#

A typical case is when you need to react to some exception by writing an except block for it, but you still have to raise the exact same exception - just to pass the information about it to a higher-level caller.

It’s definitely not an antipattern, as it is literally described in the raise section of the official documentation.

In fact, it is reflected in the language syntax. You can omit specifying the exception for raise inside an except block - it automatically re-raises the exception that caused entry into this except.


The typical behaviour of a try/except block is to suppress the exception if the corresponding except block is found. The following code shows that:

try:
    raise ValueError("hello")
except:
    pass

But the following cell demonstrates that using just the raise keyword in an except block leads to re-rising the exception.

try:
    raise ValueError("hello")
except:
    # break connection with the database
    raise
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[2], line 2
      1 try:
----> 2     raise ValueError("hello")
      3 except:
      4     # break connection with the database
      5     raise

ValueError: hello

Same except block#

To use the same code to handle different types of exceptions, you can simply mention them as tuples in the condition for the except block.


In the following example, a random exception type is displayed, but only three of four possible error types are mentioned in the except block. So I will only get a message from the except block until I get the unmentioned error type.

I would like to emphasise once again that this is not done to handle all error types, there is a special design for this which is described here.

from random import choice
test_lst = [1,2]
try_functions = {
    "ZeroDivisionError": lambda: 8/0, # 
    "IndexError" : lambda: test_lst[5],
    "TypeError" : lambda: "hello" + 4,
    "NameError" : lambda: unknown_name
}
error_types = list(try_functions.keys())

for i in range(10):
    try:
        this_type = choice(error_types)
        print(f"====I got a type {this_type}====")
        try_functions[this_type]()
    except (ZeroDivisionError, IndexError, TypeError):
        print("I handle all mentioned exceptions")
====I got a type ZeroDivisionError====
I handle all mentioned exceptions
====I got a type TypeError====
I handle all mentioned exceptions
====I got a type ZeroDivisionError====
I handle all mentioned exceptions
====I got a type ZeroDivisionError====
I handle all mentioned exceptions
====I got a type IndexError====
I handle all mentioned exceptions
====I got a type NameError====
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

Cell In[1], line 15

     13     this_type = choice(error_types)

     14     print(f"====I got a type {this_type}====")

---> 15     try_functions[this_type]()

     16 except (ZeroDivisionError, IndexError, TypeError):

     17     print("I handle all mentioned exceptions")



Cell In[1], line 7, in <lambda>()

      1 from random import choice

      2 test_lst = [1,2]

      3 try_functions = {

      4     "ZeroDivisionError": lambda: 8/0, # 

      5     "IndexError" : lambda: test_lst[5],

      6     "TypeError" : lambda: "hello" + 4,

----> 7     "NameError" : lambda: unknown_name

      8 }

      9 error_types = list(try_functions.keys())

     11 for i in range(10):



NameError: name 'unknown_name' is not defined

Different except blocks#

You can set code to handle a particular type of error, and do it several times for a try block. All you have to do is mention several except blocks one after the other.


The following example, call random error in a loop, and different errors have different handlers. You can see that there is a specific message for each iteration.

import numpy as np

test_lst = [1,2]
try_functions = {
    "ZeroDivisionError": lambda: 8/0, # 
    "IndexError" : lambda: test_lst[5],
    "TypeError" : lambda: "hello" + 4,
}
error_types = list(try_functions.keys())

for i in range(10):
    try:
        this_type = np.random.choice(error_types)
        print(f"====I got a type {this_type}====")
        try_functions[this_type]()
    except ZeroDivisionError:
        print("This is divison by zero (first option)")
    except IndexError:
        print("This is wrong index (second option)")
    except TypeError:
        print("This is wrong operations with types (third option)")
====I got a type TypeError====
This is wrong operations with types (third option)
====I got a type ZeroDivisionError====
This is divison by zero (first option)
====I got a type TypeError====
This is wrong operations with types (third option)
====I got a type IndexError====
This is wrong index (second option)
====I got a type TypeError====
This is wrong operations with types (third option)
====I got a type TypeError====
This is wrong operations with types (third option)
====I got a type ZeroDivisionError====
This is divison by zero (first option)
====I got a type TypeError====
This is wrong operations with types (third option)
====I got a type ZeroDivisionError====
This is divison by zero (first option)
====I got a type TypeError====
This is wrong operations with types (third option)

default except#

You can run some code with any type of exception, just without specifying the type of exception in the corresponding except block.


In the following example, I call random exceptions in a loop, and each of them is handled by an except block.

from random import choice

test_lst = [1,2]
try_functions = {
    "ZeroDivisionError": lambda: 8/0, # 
    "IndexError" : lambda: test_lst[5],
    "TypeError" : lambda: "hello" + 4,
    "NameError" : lambda: unknown_name
}
error_types = list(try_functions.keys())


for i in range(10):
    try:
        this_type = choice(error_types)
        print(f"====I got a type {this_type}====")
        try_functions[this_type]()
    except:
        print("I handle all exceptions")
====I got a type NameError====
I handle all exceptions
====I got a type TypeError====
I handle all exceptions
====I got a type NameError====
I handle all exceptions
====I got a type NameError====
I handle all exceptions
====I got a type NameError====
I handle all exceptions
====I got a type TypeError====
I handle all exceptions
====I got a type ZeroDivisionError====
I handle all exceptions
====I got a type IndexError====
I handle all exceptions
====I got a type ZeroDivisionError====
I handle all exceptions
====I got a type IndexError====
I handle all exceptions

Note that default except must be placed last.

So it’s not possible if the default except hadnle all exceptions, even if you specify some specific ones - specific ones always have priority.

In the following example, I am trying to place a specific exception after the default exception, but I am getting a syntax error.

try:
    "str" + 1
except:
    print("I handle all exceptions")
except TypeError:
    print("I handle type error")
  Cell In[56], line 3

    except:

    ^

SyntaxError: default 'except:' must be last