Exception handling in Python

Coding with Python

🕑 This lesson will take about 20 minutes

Python includes two features for handling exceptions (any unexpected errors) and to help with debugging your programs:

  • Exception handling

  • Assertions

When an exception is raised, you can check what the problem is and display an appropriate error message or run some other code.

Here are some common standard exceptions in Python:

  • ArithmeticError - The base class for all errors that occur from a numeric calculation.

  • OverflowError - This exception is raised when a calculation exceeds the maximum limit for a numeric type.

  • ZeroDivisionError - This exception is raised when division (or modulo) by zero occurs for all numeric types.

  • AttributeError - An exception that is raised in case of failure of attribute reference or assignment.

  • EOFError - This exception is raised when there is no input provided from either the raw_input() or input() function and the end of file is reached.

  • ImportError - Exception that is raised when an import statement fails.

  • IndexError - This exception is raised when an index of a specified sequence (such as a list, tuple or string) does not exist.

  • KeyError - Like the IndexError, but this exception is raised when a specified key is not found in a dictionary.

  • NameError - This exception is raised when an identifier (eg. variable name) is not found in the local or global namespace.

  • IOError - This exception is raised when an IO (input/output) operation fails, for example, when trying to open a file that does not exist. An exception can be raised for operating system-related errors.

  • SyntaxError - This exception is raised when there is an error in Python code syntax (the code does not follow the rules of the language). This is probably the most common error type you will see when you begin programming.

  • TypeError - An exception raised when an operation or function that is invalid for the specified data type is attempted.

  • ValueError This exception is raised when the built-in function for a data type has the valid type of arguments, but the arguments have invalid values specified.

  • RuntimeError This exception is raised when a generated error doesn't fall into any category.

You can find the full list of Python exception types here.

Assertions

An assertion is a check that can be used when testing your program. An assertion involves the testing of an expression, where if the result is False, an exception will be raised. Programmers often use assertions at the beginning of a function to check that the provided input is valid, and after a function call to check that the output is also valid.

The assert statement

When Python encounters an assert statement, Python will evaluate the provided expression, which is intended to return True. However, if the expression is False, Python will raise an AssertionError exception. If the assertion fails, Python will use the ArgumentExpression as the argument for the AssertionError.

AssertionError exceptions can be caught and handled like any other type of exception using the try-except statement. However, if not handled, an AssertionError exception will terminate the program and produce a traceback.

Example

Check out the example below. The function inchesToCM() below has one parameter. It will take a number value (length, in inches) as an argument, convert it to length in centimetres, and then return the result. The assert statement is used to check the expression length > 0 (to make sure the length provided is greater than 0). If the expression returns False (in other words, if the length provided when the function is called is less than or equal to 0), then an AssertionError exception will be raised and the message provided (eg. "Length must be greater than 0") will be displayed along with the AssertionError.

The assert keyword is followed by an expression, a comma, and then a message to display if the expression evaluates to False when the line of code is executed.

When using the print() function to output the result of the three function calls (lines 5-7 in the example code above), the first would display 81.28, and the second would produce an AssertionError. As an error has been detected, the third function call on line 7 will be prevented from running (but it would also produce an AssertionError if it was called).

Exceptions

An exception is an event that occurs during the execution (running) of a program that will disrupt the normal flow of the program's instructions. Generally speaking, when a Python program encounters a problem, it will raise an exception. An exception is a Python object representing an error. When a Python program raises an exception, it will need to either handle the exception immediately, otherwise the program will terminate execution and quit.

Handling exceptions

If you have written some code that you suspect could raise an exception, you can protect the program from terminating early (crashing or quitting) by placing the “sketchy” code in a try: block. After the try: block, you will need to include an except: statement, which is then followed by a block of code that will handle the problem in the best way possible.

Example

Let's have a look at an example. We have a function called DivideCookies() which has two parameters:

  • numberOfCookies

  • numberOfPeople.

The function will work out how to evenly divide cookies between a group of people without worrying about how many cookies are left over. It will use the // floor division operator to get a whole number result and return this value when the function is called.

However, we are a little worried that the program will crash if 0 is provided as the value for numberOfPeople. We can't divide by zero! We want to ensure the program doesn't crash if the program tries to divide a number of cookies amongst 0 people, which would result in a ZeroDivisionError. So, we have a try...except...else statement that will attempt to run the DivideCookies function. If a ZeroDivisionError exception is detected, then it will be handled by displaying a suitable error message and the rest of the program will continue to execute. If all is ok and no ZeroDivisionError exception is detected, then the result of the division will be displayed to the user (in the else statement) and the rest of the program will also continue to run.

In the example below, the first time we call the function, it will cause a ZeroDivisionError error (as 0 has been provided for the numberOfPeople parameter. It will display an error message “Number of people must be greater than 0” rather than just crashing the program. After this, the program will continue to run and will call the function providing the value 2 for the numberOfPeople parameter. The output will be “Number of cookies per person: 2”.

Using the example scenario above, if we didn’t use the try..except..else statements and a ZeroDivisionError was generated, then the entire program would crash and stop running. Obviously, we want to make sure our code is reliable and we should ensure that all types of errors are covered. For example, we should add some input data validation code to ensure that “bad” input can’t even be entered by the user in the first place. It’s not a great idea to patch every problem with a try..except..else statement. However, there are scenarios where exception handling might be needed.

Using the except clause with no exceptions defined

In the example program above, there are a number of things that can go wrong when calling the DivideCookies() function. For example, we could try to divide by zero resulting in an error. We could also try to divide a string value which would also result in an error (eg "a"//2). Or we could even call the function without providing any values for the numberOfCookies and numberOfPeople parameters.

We can actually use the except: clause without specifying a specific type of exception which means that any type of exception will be handled. This is kind of a "catch all" approach. Check out the example below. In this example, no exception has been defined after the except: clause which means that if any type of error is detected, a generic response will be given.

Experiment with the code above by changing the arguments provided for the numberOfCookies and numberOfPeople parameters (eg. provide a string value, or a 0, empty arguments, or incorrect number of arguments). The same generic response will be provided when any type of error is detected and the rest of the code will continue running without the program crashing.

This type of try-except statement catches all of the exceptions that could possibly occur. However, taking this kind of approach is not considered a good programming practice. It might catch all exceptions but it doesn't help the programmer identify and fix the root cause of a problem. The best approach is to write good code that is thoroughly tested and capable of working in a variety of situations.

Using the except clause with multiple exceptions

Another way we can use the except: clause is by specifying multiple exceptions (inside parantheses, separate by commas) to handle more than one exception. Take a look at the example below. This code will handle both a ZeroDivisionError and a TypeError exception, and then display an appropriate message if either of these exceptions are detected.

Experiment with the code above by changing the error types in the except clause (experiment with different types of errors) and changing the values provided as arguments for the function’s parameters. You’ll notice that the code will handle ZeroDivisionError and TypeError errors but if any other type of error is generated, then the program will crash.

The try-finally clause

The finally: block can be used along with a try: block. The finally block is used to contain any code that must execute, regardless of whether the try-block raised an exception or not. In the example below, the print() function on line 11 will always execute, regardless of whether an exception was raised or not.

Capturing an exception's argument

Exceptions can have an argument (a value that gives additional information about the problem) which can be useful when debugging your code. You capture an exception's argument by providing a variable in the except clause as shown in the example below. In this example, the program will attempt to divide 5 by 0 causing an error. A custom message will be displayed identifying the type of error that occurred.

Raising an exception

You can actually raise exceptions in different ways by using the raise statement as shown below. In the example below, we have a function that will take a phone number (stored as a string value) and check if the length of the phone number is not equal to 10 characters. If the phone number is not 10 characters long, then it is not a phone number. We could also check to make sure the phone number only contains digits 0-9 but we won't bother with that here. If the phone number is not 10 characters in length, then an exception will be raised. When the exception is raised, we can use the phoneNumber variable and refer to the exception as InvalidNumber.

In the example above, the following scenarios could occur:

  • An invalid phone number (not equal to 10 characters in length) is provided:

    • Only the “Phone number [phone number] is not valid” message will be displayed

  • A valid phone number (equal to 10 characters in length) is provided

    • The “The phone number is [phone number]” message will be displayed.

    • The “Bye!” message will also be displayed.


Next lesson: Working with files