Exceptions#

Exceptions is a cases in program when it is syntactically correct but there some other cases doesn’t expected to be normal.

Check more on the:

Several exception types#

If you want to handle multiple exceptions for a block, you can use one of the following options:

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)

Ony one exception per type#

You can define any number of except blocks for the same exception type, but only the first one will be called.


In the following example, even though is declared two codes for the ZeroDivisionError type exception, only the first one was executed.

try:
    1/0
except ZeroDivisionError:
    print("First code to handle exception")
except ZeroDivisionError:
    print("Second code to handle exception")
First code to handle exception

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

Except variable#

In the except clause, you can be specify a variable that will hold the instance of the exception that occurred - you must use the syntax except <Exception> as <variable name> as the result in the corresponding block can implement logic that works with <variable name>. This variable will refer to the instance of the exception that occurred.


The following cell shows how different exceptions pass the corresponding object to the variable of the except block.

try_functions = [
    lambda: 8/0,
    lambda: "hello" + 4
]

for fun in try_functions:
    try:
        fun()
        print("no error")
    except Exception as e:
        print(type(e))
        print("error type:", e)
<class 'ZeroDivisionError'>
error type: division by zero
<class 'TypeError'>
error type: can only concatenate str (not "int") to str

From sys#

In the sys built-in python module, there are two functions, sys.exception() and sys.exc_info(), which can also provide information about exceptions that have occurred. See the corresponding section of the official documentation for more information.


The following cell shows that sys.exception returns exactly the same object of the exception.

import sys

try: 9/0
except Exception as e: print(sys.exception() is e) 
True

sys.exc_info is an old fashioned function that returns a tuple with different compoments of the exception.

try: "10" + 10
except: print(sys.exc_info())
(<class 'TypeError'>, TypeError('can only concatenate str (not "int") to str'), <traceback object at 0x799ef7ff6200>)

Traceback#

Traceback is information about where in the code an exception occurred. It can be extracted from the exception instance using __traceback__ attribute. Practically you have to use traceback.format_exception to prepare traceback to the view as it is printed to the output in the python programm. Read more about traceback objects on the corresponding page of the official documentation.


The following cell stores the exception instance that will be used as an example.

try:
    10/0
except Exception as e:
    val = e 

It holds few attributes that actually hold all the information from which a typical traceback message can be constructed.

traceback = val.__traceback__
print(traceback.tb_frame)
print(traceback.tb_lineno)
print(traceback.tb_lasti)
<frame at 0x799ef7facca0, file '/tmp/ipykernel_8011/2685439381.py', line 4, code <module>>
2
8

The following cell shows the use of the traceback.format_exception function to construct a typical traceback message.

import traceback
ans = traceback.format_exception(type(val), val, val.__traceback__)
print("".join(ans))
Traceback (most recent call last):
  File "/tmp/ipykernel_8011/2685439381.py", line 2, in <module>
    10/0
    ~~^~
ZeroDivisionError: division by zero