Exception handling in python

What is exception?
During program execution, when an unusual or unexpected condition arises which the program cannot handle, it raises an exception. Example of unusual conditions include division of a number by 0, opening a file which does not exist, writing to a socket which is closed, accessing an array location beyond its size etc.


What is exception handling and why do it?
Handling an exception means dealing with the unusual conditions so that the application does not exit abnormally. Exception handling should be performed to make an application stable and user friendly. Exception handling enables an application to show proper error messages and reason of failure. Applications with proper error handling are reliable and trustworthy.
An exception can be raised due to user’s mistake or a bug in the application itself. Exception handling enables a user to rectify a mistake if it is at his end or report an error if it is from the application end.
Imagine an application reads a file selected by the user and loads its contents in a window. Now user selects a file and the application window goes away. This is because the file he selected did not have read permission and the application blew away. But the user has no clue as to what exactly happened.
An application with exceptions handled properly would show an error message to the user informing him that the selected file does not have read permission so that he can give the permissions or load another file.
Python exception handling support
Exception handling is a vital part of any programming language and is supported by python as well. Python provides a few keywords which are used to handle exceptions. They are try, except, else and finally.
Syntax is:

try:
   # risky code
   # risky code
except:
   # handle error
finally:
   # execute always

try block contains code which might raise an error. This depends on the discretion of the programmer as to which code might raise an error. If the code raises an error, the control skips and moves to the except block which contains handling for the error(such as showing error messages or writing them to log file). Then the control moves to finally block.


Out of the above three keywords, try is mandatory while except and finally are optional but one of these should be present. That is, a combination of tryexcept, tryfinally and tryexceptfinally is permissible. Also, finally block is always executed, whether there is an exception raised or not.

When an error occurs inside try block, lines following the error are not executed and the control moves directly to except or finally block.

Also, except keyword could be followed by the specific exception that needs to be handled. If it is not followed by any exception type, this means that it will handle all exceptions. This approach is not recommended since you will not be able to identify the reason of origin of error.
Remember that in the absence of exception handling, if there is any error raised then it is handled by the runtime environment of the programming language.
Exception handling example
Following code example will clarify the concept of exception handling in python so that we can dig deeper into it.

try:
    # read a number from keyboard
    value = input("Enter a value\n")
    # divide 100 by the entered value
    result = 100/int(value)
    # print result
    print("Result of division is",result)
except ZeroDivisionError:
    # print error message
    print("Division by zero")
finally:
    print("Normal program termination")

Above program reads a value from the keyboard, divides 100 by the input value and prints the result. This code might be risky since we do not know the input value. Thus, it is embedded inside a try block followed by an except block which handles a ZeroDivisionError. Thus, if the input value is 0, then the program is equipped to handle it. except block is followed by a finally block which will execute irrespective of the value entered.

Sample run of the above program produces the following output.

Enter a value
0
Division by zero
Normal program termination

Output clearly shows that for input value 0, division fails due to which except block is executed followed by finally block execution.
Note that except block if followed by an exception will only handle errors that are covered by that exception type. Thus, catching ZeroDivisionError will only enable except block to execute when there is an exception due to division by 0. Following sample output of above code demonstrates it.

Enter a value
abcd
Traceback (most recent call last):
File “C:/exception.py”, line 5, in <module>
result = 100/int(value)
ValueError: invalid literal for int() with base 10: ‘abcd’
Normal program termination

For the input value abcd, the program failed with the system generated error. This is because the program raised a ValueError but the except block was written to handle ZeroDivisionError and so it could not catch the other error. Note that finally block still executed.
Handling multiple exceptions
In the above scenario despite adding exception handling, the program failed abnormally. This is because it expected only errors due to division by 0 but ignored invalid input values(such as letters, decimal numbers or special characters etc.). Fortunately, python provides handling multiple exception types. There are two ways to handle more than one exception types in a single try block. They are
1. Using multiple except blocks
You may add any number of except blocks for handling different types of errors for a single try block. Only the except block which matches the type of error will be executed, rest all will be skipped. Example,

try:
    # read a number from keyboard
    value = input("Enter a value\n")
    # divide 100 by the entered value
    result = 100/int(value)
    # print result
    print("Result of division is",result)
except ValueError:
    # error due to wrong input()
    print("Invalid value entered")
except ZeroDivisionError:
    # print error message
    print("Division by zero")
except:
    # handle any other error
    print("Unexpected error")

Above try block is followed by 3 except blocks for handling different error types. Last except block is for handling any error apart from ValueError and ZeroDivisionError. Sample runs of the program will make it more clear.

Enter a value
0
Division by zero

Enter a value
abcd
Invalid value entered

It is thus understood from the executions that for different error types, appropriate except blocks are being executed.

2. Multiple exception types in same except block
It is also possible for a single except block to handle more than one exception types. All the exception types are enclosed within parenthesis and separated by a comma and written after except as shown below.

try:
    # read a number from keyboard
    value = input("Enter a value\n")
    # divide 100 by the entered value
    result = 100/int(value)
    # print result
    print("Result of division is",result)
except (ValueError, ZeroDivisionError):
    # error due to wrong input()
    print("Invalid value or zero entered")

Above except block will handle input values 0, letters and special characters.
else block
There are scenarios when you want some code to execute when there was no error raised. For such cases, else block is used. This block should be placed after except block(s) but before finally block. If there is any error, appropriate except block is executed and if there is no error, then else block is executed. In no case, both except and else blocks are executed together. Example,

try:
    # read a number from keyboard
    value = input("Enter a value\n")
    # divide 100 by the entered value
    result = 100/int(value)
    # print result
    print("Result of division is",result)
except (ValueError, ZeroDivisionError):
    # error due to wrong input()
    print("Invalid value or zero entered")
else:
    # no error
    print("Correct value entered")

Output of execution of above code is

Enter a value
10
Result of division is 10.0
Correct value entered

Nested blocks
You can nest tryexcept block inside another block. There is no limit to the level of nesting. One might ask why such nesting is required. It might happen that nested block contains some risky code but you want to execute some code which is outside inner block but inside outer block even if there is some error in inner block code execution. Nesting of tryexcept block becomes handy in such scenarios. Example,

try:
    value = input("Enter a number")
    # inner block
    try:
        result = 100/int(value)
    except ZeroDivisionError:
        print("Division by zero not allowed")
    # will execute even if there is some error in inner block
    print("Value entered is",value)
except ValueError:
    print("Error in reading input")

Remember that if there is finally block with the inner block, then it will be executed before the code after the inner block is executed.
Throwing or raising error
There are times when it becomes necessary to throw or raise an error if some condition is met. This is done when there is no benefit of further execution. Example, you want to read a file and check for its presence. If the file does not exist, there is no point in moving forward to read the file as this will not succeed. It is better to throw an error in such case.
Python provides raise keyword for raising an error manually. This is followed by the type of error you want to raise. Example,

try:
    # read a number from keyboard
    value = input("Enter a value\n")
    # check for 0
    if value == '0':
        raise ZeroDivisionError
    # divide 100 by the entered value
    result = 100/int(value)
    # print result
    print("Result of division is",result)
except ZeroDivisionError:
    # error due to wrong input()
    print("Division by zero not allowed")

Above code compares the entered value with ‘0’ and raises a ZeroDivisionError if it is ‘0’ which is then handled by enclosing except block. Output follows.

Enter a value
0
Division by zero not allowed

Custom error messages when raising error
You can also provide your own error messages when raising an error and these messages can be accessed inside except block. Custom error message should be enclosed inside parenthesis and written after the error is raised. For accessing the error message, you need to write a variable name preceded with as keyword after the error being handled in except block. An example will clarify it.

try:
    # read a number from keyboard
    value = input("Enter a value\n")
    # check for 0
    if value == '0':
        # raise error with a custom message
        raise ZeroDivisionError("This will result in error")
    # divide 100 by the entered value
    result = 100/int(value)
    # print result
    print("Result of division is",result)
# associate a variable with the error
except ZeroDivisionError as ze:
    # print custom message
    print(ze)

Note that when raising an error, a custom message is supplied and when handling the error in except block, a variable name is supplied preceded with as keyword. This variable name is then directly supplied to the print statement. Above code when executed with an input value of 0 results in the following output.

Enter a number
0
This will result in error

Let’s tweak in

  1. If a line of code in try block results in an error then all the lines following it are skipped.
  2. finally block will always execute if it is present.
  3. else block will only execute when there is no error and except is not executed when else is executed.
  4. finally block can also contain tryexcept block.
  5. Explicit error can also be raised from finally block.
  6. Variable name after as can be any user defined name.
  7. tryexcept block can be nested inside another tryexcept block.

Mark Your Impression

Close Menu