Python Exceptions Handling Using try, except and finally statement#

Learn how to handle exceptions in your Python program using try, except and finally statements with the help of examples.

Exceptions in Python#

An exception is an event, which occurs during the execution of a program that disrupts the normal flow of the program’s instructions. In general, when a Python script encounters a situation that it cannot cope with, it raises an exception. An exception is a Python object that represents an error.

Python has many built-in exceptions that are raised when your program encounters an error (something in the program goes wrong).

When a Python script raises an exception, it must either handle the exception immediately otherwise it terminates and quits.

When these exceptions occur, the Python interpreter stops the current process and passes it to the calling process until it is handled. If not handled, the program will crash.

For example, let us consider a program where we have a function A that calls function B, which in turn calls function C. If an exception occurs in function C but is not handled in C, the exception passes to B and then to A.

If never handled, an error message is displayed and our program comes to a sudden unexpected halt.

Catching Exceptions in Python#

In Python, exceptions can be handled using a try statement.

The critical operation which can raise an exception is placed inside the try clause. The code that handles the exceptions is written in the except clause.

We can thus choose what operations to perform once we have caught the exception. Here is a simple example.

# Example 1:

try:
    print(10 + '5')
except:
    print('Something went wrong')
Something went wrong

Explanation:

In the example above the second operand is a string. We could change it to float or int to add it with the number to make it work. But without any changes, the second block, except, will be executed.

# Example 2:

# import module sys to get the type of exception
import sys

randomList = ['a', 0, 2]

for entry in randomList:
    try:
        print("The entry is", entry)
        r = 1/int(entry)
        break
    except:
        print("Oops!", sys.exc_info()[0], "occurred.")
        print("Next entry.")
        print()
print("The reciprocal of", entry, "is", r)
The entry is a
Oops! <class 'ValueError'> occurred.
Next entry.

The entry is 0
Oops! <class 'ZeroDivisionError'> occurred.
Next entry.

The entry is 2
The reciprocal of 2 is 0.5

Explanation:

In this program, we loop through the values of the randomList list. As previously mentioned, the portion that can cause an exception is placed inside the try block.

If no exception occurs, the except block is skipped and normal flow continues(for last value). But if any exception occurs, it is caught by the except block (first and second values).

Here, we print the name of the exception using the exc_info() function inside sys module. We can see that a causes ValueError and 0 causes ZeroDivisionError.

Since every exception in Python inherits from the base Exception class, we can also perform the above task in the following way:

# Example 3:

# This program has the same output as the above program.

# import module sys to get the type of exception
import sys

randomList = ['a', 0, 2]

for entry in randomList:
    try:
        print("The entry is", entry)
        r = 1/int(entry)
        break
    except Exception as e:
        print("Oops!", e.__class__, "occurred.")
        print("Next entry.")
        print()
print("The reciprocal of", entry, "is", r)
The entry is a
Oops! <class 'ValueError'> occurred.
Next entry.

The entry is 0
Oops! <class 'ZeroDivisionError'> occurred.
Next entry.

The entry is 2
The reciprocal of 2 is 0.5
# Example 4:

try:
    name = input('Enter your name: ')
    year_born = input('Year you were born: ')
    age = 2022 - year_born
    print(f'You are {name}. And your age is {age}.')
except:
    print('Something went wrong')
Enter your name: Milaan
Year you were born: 1926
Something went wrong

Explanation:

In the above example, the exception block will run and we do not know exactly the problem. To analyze the problem, we can use the different error types with except.

In the following example, it will handle the error and will also tell us the kind of error raised.

# Example 5:

try:
    name = input('Enter your name: ')
    year_born = input('Year you were born: ')
    age = 2022 - year_born
    print(f'You are {name}. And your age is {age}.')
except TypeError:
    print('Type error occured')
except ValueError:
    print('Value error occured')
except ZeroDivisionError:
    print('zero division error occured')
Enter your name: Milaan
Year you were born: 1926
Type error occured

Catching Specific Exceptions in Python#

In the above example, we did not mention any specific exception in the except clause.

This is not a good programming practice as it will catch all exceptions and handle every case in the same way. We can specify which exceptions an except clause should catch.

A try clause can have any number of except clauses to handle different exceptions, however, only one will be executed in case an exception occurs.

We can use a tuple of values to specify multiple exceptions in an except clause. Here is an example pseudo code.

>>> try:
>>>    # do something
>>>    pass

>>> except ValueError:
>>>    # handle ValueError exception
>>>    pass

>>> except (TypeError, ZeroDivisionError):
>>>    # handle multiple exceptions
>>>    # TypeError and ZeroDivisionError
>>>    pass

>>> except:
>>>    # handle all other exceptions
>>>    pass

Raising Exceptions in Python#

In Python programming, exceptions are raised when errors occur at runtime. We can also manually raise exceptions using the raise keyword.

We can optionally pass values to the exception to clarify why that exception was raised.

raise KeyboardInterrupt
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-6-c761920b81b0> in <module>
----> 1 raise KeyboardInterrupt

KeyboardInterrupt: 
raise MemoryError("This is an argument")
---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-7-51782e52f201> in <module>
----> 1 raise MemoryError("This is an argument")

MemoryError: This is an argument
try:
    a = int(input("Enter a positive integer: "))
    if a <= 0:
        raise ValueError("That is not a positive number!")
except ValueError as ve:
    print(ve)
Enter a positive integer: 9

Python try with else clause#

In some situations, you might want to run a certain block of code if the code block inside try ran without any errors. For these cases, you can use the optional else keyword with the try statement.

Note: Exceptions in the else clause are not handled by the preceding except clauses.

>>> try:
>>>    You do your operations here
>>>    ......................
>>> except ExceptionI:
>>>    If there is ExceptionI, then execute this block.
>>> except ExceptionII:
>>>    If there is ExceptionII, then execute this block.
>>>    ......................
>>> else:
>>>    If there is no exception then execute this block.

With our “Hello, World!” program written, we are ready to run the program. We’ll use the python3 command along with the name of our program file. Let’s run the program:

  • A single try statement can have multiple except statements. This is useful when the try block contains statements that may throw different types of exceptions.

  • You can also provide a generic except clause, which handles any exception.

  • After the except clause(s), you can include an else-clause. The code in the else-block executes if the code in the try: block does not raise an exception.

  • The else-block is a good place for code that does not need the try: block’s protection.

# Example 1:

try:
    fh = open("testfile.txt", "w")
    fh.write("This is my test file for exception handling!!")
except IOError:
    print ("Error: can\'t find file or read data")
else:
    print ("Written content in the file successfully")
    fh.close()
Written content in the file successfully
# Example 2: program to print the reciprocal of even numbers

try:
    num = int(input("Enter a number: "))
    assert num % 2 == 0
except:
    print("Not an even number!")
else:
    reciprocal = 1/num
    print(reciprocal)
Enter a number: 63
Not an even number!

Explanation:

If we pass an odd number:

Enter a number: 1
Not an even number!

If we pass an even number, the reciprocal is computed and displayed.

Enter a number: 4
0.25

However, if we pass 0, we get ZeroDivisionError as the code block inside else is not handled by preceding except.

Enter a number: 0
Traceback (most recent call last):
  File "<string>", line 7, in <module>
    reciprocal = 1/num
ZeroDivisionError: division by zero

The except Clause with No Exceptions#

You can also use the except statement with no exceptions defined as follows

>>> try:
>>>    You do your operations here
>>>    ......................
>>> except:
>>>    If there is any exception, then execute this block.
>>>    ......................
>>> else:
>>>    If there is no exception then execute this block

This kind of a try-except statement catches all the exceptions that occur. Using this kind of try-except statement is not considered a good programming practice though, because it catches all exceptions but does not make the programmer identify the root cause of the problem that may occur.

The except Clause with Multiple Exceptions#

You can also use the same except statement to handle multiple exceptions as follows −

>>>try:
>>>   You do your operations here
>>>   ......................
>>>except(Exception1[, Exception2[,...ExceptionN]]]):
>>>   If there is any exception from the given exception list,
>>>   then execute this block.
>>>   ......................
>>>else:
>>>   If there is no exception then execute this block.

Python try-finally Clause#

The try statement in Python can have an optional finally clause. This clause is executed no matter what, and is generally used to release external resources.

For example, we may be connected to a remote data center through the network or working with a file or a Graphical User Interface (GUI).

In all these circumstances, we must clean up the resource before the program comes to a halt whether it successfully ran or not. These actions (closing a file, GUI or disconnecting from network) are performed in the finally clause to guarantee the execution.

You can use a finally: block along with a try: block. The finally: block is a place to put any code that must execute, whether the try-block raised an exception or not. The syntax of the try-finally statement is this −

>>> try:
>>>    You do your operations here;
>>>     ......................
>>>    Due to any exception, this may be skipped.
>>> finally:
>>>    This would always be executed.
>>>    ......................

Note: You can provide except clause(s), or a finally clause, but not both. You cannot use else clause as well along with a finally clause.

try:
    fh = open("testfile.txt", "w")
    fh.write("This is my test file for exception handling!!")
finally:
    print ("Error: can\'t find file or read data")
    fh.close()
    
# If you do not have permission to open the file in writing mode, 
# then this will produce the following result −
Error: can't find file or read data

Here is an example of file operations to illustrate this.

try:
    f = open("testfile.txt",encoding = 'utf-8')
    # perform file operations
finally:
    f.close()

Explanation:

This type of construct makes sure that the file is closed even if an exception occurs during the program execution.

In the # Example 5: code above the output is going to be TypeError. Now, let’s add an additional block:

# Example:

try:
    name = input('Enter your name: ')
    year_born = input('Year you born: ')
    age = 2022 - int(year_born)
    print(f'You are {name}. And your age is {age}.')
except TypeError:
    print('Type error occur')
except ValueError:
    print('Value error occur')
except ZeroDivisionError:
    print('zero division error occur')
else:
    print('I usually run with the try block')
finally:
    print('I alway run.')
Enter your name: Milaan
Year you born: 1926
You are Milaan. And your age is 96.
I usually run with the try block
I alway run.

It is also shorten the above code as follows:

try:
    name = input('Enter your name: ')
    year_born = input('Year you born: ')
    age = 2022 - int(year_born)
    print(f'You are {name}. And your age is {age}.')
except Exception as e:
    print(e)
Enter your name: Milaan
Year you born: 1926
You are Milaan. And your age is 96.

Argument of an Exception#

An exception can have an argument, which is a value that gives additional information about the problem. The contents of the argument vary by exception. You capture an exception’s argument by supplying a variable in the except clause as follows −

# Define a function here.
def temp_convert(var):
    try:
        return int(var)
    except ValueError as Argument:
        print ("The argument does not contain numbers\n", Argument)

# Call above function here.
temp_convert("xyz")
The argument does not contain numbers
 invalid literal for int() with base 10: 'xyz'

Raising an Exception#

You can raise exceptions in several ways by using the raise statement.

The general syntax for the raise statement is as follows:

raise [Exception [, args [, traceback]]]

Here, Exception is the type of exception (for example, NameError) and argument is a value for the exception argument. The argument is optional; if not supplied, the exception argument is None.

The final argument, traceback, is also optional (and rarely used in practice), and if present, is the traceback object used for the exception.

def functionName( level ):
    if level < 1:
        raise Exception(level)
        # The code below to this would not be executed
        # if we raise the exception
    return level

try:
    l = functionName(-10)
    print ("level = ",l)
except Exception as e:
    print ("error in level argument",e.args[0])
error in level argument -10

Writing the “Hello, World!” Program#

Python also allows you to create your own exceptions by deriving classes from the standard built-in exceptions.

Here is an example related to RuntimeError. Here, a class is created that is subclassed from RuntimeError. This is useful when you need to display more specific information when an exception is caught.

In the try block, the user-defined exception is raised and caught in the except block. The variable e is used to create an instance of the class Networkerror.

>>> class Networkerror(RuntimeError):
>>>     def _init_(self, arg):
>>>        self.args = arg

So once you have defined the above class, you can raise the exception as follows −

>>> try:
>>>    raise Networkerror("Bad hostname")
>>> except Networkerror,e:
>>>    print(e.args)

Packing and Unpacking Arguments in Python#

We use two operators:

  • * for tuples

  • ** for dictionaries

Let us take as an example below. It takes only arguments but we have list. We can unpack the list and changes to argument.

Unpacking#

Unpacking Lists#

def sum_of_five_nums(a, b, c, d, e):
    return a + b + c + d + e

lst = [1, 2, 3, 4, 5]
print(sum_of_five_nums(lst)) # TypeError: sum_of_five_nums() missing 4 required positional arguments: 'b', 'c', 'd', and 'e'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-b951d4fef2c8> in <module>
      3 
      4 lst = [1, 2, 3, 4, 5]
----> 5 print(sum_of_five_nums(lst)) # TypeError: sum_of_five_nums() missing 4 required positional arguments: 'b', 'c', 'd', and 'e'

TypeError: sum_of_five_nums() missing 4 required positional arguments: 'b', 'c', 'd', and 'e'

When we run the above code, it raises an error, because this function takes numbers (not a list) as arguments. Let us unpack/destructure the list.

def sum_of_five_nums(a, b, c, d, e):
    return a + b + c + d + e

lst = [1, 2, 3, 4, 5]
print(sum_of_five_nums(*lst))  # 15
15

We can also use unpacking in the range() built-in function that expects a start and an end.

numbers = range(2, 7)  # normal call with separate arguments
print(list(numbers)) # [2, 3, 4, 5, 6]
args = [2, 7]
numbers = range(*args)  # call with arguments unpacked from a list
print(numbers)      # [2, 3, 4, 5,6]
[2, 3, 4, 5, 6]
range(2, 7)

Unpacking List or a Tuple#

countries = ['Finland', 'Sweden', 'Norway', 'Denmark', 'Iceland']
fin, sw, nor, *rest = countries
print(fin, sw, nor, rest)   # Finland Sweden Norway ['Denmark', 'Iceland']
numbers = [1, 2, 3, 4, 5, 6, 7]
one, *middle, last = numbers
print(one, middle, last)      #  1 [2, 3, 4, 5, 6] 7
Finland Sweden Norway ['Denmark', 'Iceland']
1 [2, 3, 4, 5, 6] 7

Unpacking Dictionaries#

def unpacking_person_info(name, country, city, age):
    return f'{name} lives in {country}, {city}. He is {age} year old.'
dct = {'name':'Milaan', 'country':'England', 'city':'London', 'age':96}
print(unpacking_person_info(**dct)) # Milaan lives in England, London. He is 96 year old.
Milaan lives in England, London. He is 96 year old.

Packing#

Sometimes we never know how many arguments need to be passed to a python function. We can use the packing method to allow our function to take unlimited number or arbitrary number of arguments.

Packing Lists#

def sum_all(*args):
    s = 0
    for i in args:
        s += i
    return s
print(sum_all(1, 2, 3))             # 6
print(sum_all(1, 2, 3, 4, 5, 6, 7)) # 28
6
28

Packing Dictionaries#

def packing_person_info(**kwargs):
    # check the type of kwargs and it is a dict type
    # print(type(kwargs))
        # Printing dictionary items
    for key in kwargs:
        print("{key} = {kwargs[key]}")
    return kwargs

print(packing_person_info(name="Milaan",
      country="England", city="London", age=96))
{key} = {kwargs[key]}
{key} = {kwargs[key]}
{key} = {kwargs[key]}
{key} = {kwargs[key]}
{'name': 'Milaan', 'country': 'England', 'city': 'London', 'age': 96}

Spreading in Python#

Like in JavaScript, spreading is possible in Python. Let us check it in an example below:

lst_one = [1, 2, 3]
lst_two = [4, 5, 6, 7]
lst = [0, *lst_one, *lst_two]
print(lst)          # [0, 1, 2, 3, 4, 5, 6, 7]
country_lst_one = ['Finland', 'Sweden', 'Norway']
country_lst_two = ['Denmark', 'Iceland']
nordic_countries = [*country_lst_one, *country_lst_two]
print(nordic_countries)  # ['Finland', 'Sweden', 'Norway', 'Denmark', 'Iceland']
[0, 1, 2, 3, 4, 5, 6, 7]
['Finland', 'Sweden', 'Norway', 'Denmark', 'Iceland']

Enumerate#

If we are interested in an index of a list, we use enumerate built-in function to get the index of each item in the list.

for index, item in enumerate([20, 30, 40]):
    print(index, item)
0 20
1 30
2 40
for index, i in enumerate(countries):
    print('hi')
    if i == 'Finland':
        print('The country {i} has been found at index {index}')
hi
The country {i} has been found at index {index}
hi
hi
hi
hi

Zip#

Sometimes we would like to combine lists when looping through them. See the example below:

fruits = ['banana', 'orange', 'mango', 'lemon', 'lime']                    
vegetables = ['Tomato', 'Potato', 'Cabbage','Onion', 'Carrot']
fruits_and_veges = []
for f, v in zip(fruits, vegetables):
    fruits_and_veges.append({'fruit':f, 'veg':v})

print(fruits_and_veges)
[{'fruit': 'banana', 'veg': 'Tomato'}, {'fruit': 'orange', 'veg': 'Potato'}, {'fruit': 'mango', 'veg': 'Cabbage'}, {'fruit': 'lemon', 'veg': 'Onion'}, {'fruit': 'lime', 'veg': 'Carrot'}]