Skip to main content
HomeAbout PythonLearn Python

Exception & Error Handling in Python

Errors and exceptions can lead to program failure or unexpected behavior, and Python comes with a robust set of tools to improve the stability of the code.
Updated Feb 2023  · 21 min read

Run and edit the code from this tutorial online

Open Workspace

try except else finally process in Python

Errors and exceptions can lead to unexpected behavior or even stop a program from executing. Python provides various functions and mechanisms to handle these issues and improve the robustness of the code. In this tutorial, we will learn about various error types and learn about built-in functions with examples. 

An error is an issue in a program that prevents the program from completing its task. In comparison, an exception is a condition that interrupts the normal flow of the program. Both errors and exceptions are a type of runtime error, which means they occur during the execution of a program. 

In simple words, the error is a critical issue that a normal application should not catch, while an exception is a condition that a program should catch. 

Let’s learn more about errors and exceptions by looking at various examples. 

Errors in Python

Here is an example of a syntax error where a return outside the function means nothing. We should not handle errors in a program. Instead, we must create a function that returns the string. 

return "DataCamp"
Input In [1]
    return "DataCamp"
    ^
SyntaxError: 'return' outside function

We have created the function, but with the wrong indentation. We should not handle indentation errors at runtime. Either we do it manually or use code formatting tools.

def fun():
return "DataCamp"    
Input In [2]
    return "DataCamp"
    ^
IndentationError: expected an indented block

Exceptions in Python

We have encountered a ZeroDivisionError (Exception). We can handle it at runtime by using `try` and `except` blocks.

test = 1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 test = 1/0

ZeroDivisionError: division by zero

NameError exceptions are quite common where a variable is not found. We can also handle the exception either by replacing the variable or printing the warning. 

y = test
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [5], in <cell line: 1>()
----> 1 y = test

NameError

Built-in Python Exceptions

Here is the list of default Python exceptions with descriptions:

  1. AssertionError: raised when the assert statement fails.
  2. EOFError: raised when the input() function meets the end-of-file condition.
  3. AttributeError: raised when the attribute assignment or reference fails.
  4. TabError: raised when the indentations consist of inconsistent tabs or spaces. 
  5. ImportError: raised when importing the module fails. 
  6. IndexError: occurs when the index of a sequence is out of range
  7. KeyboardInterrupt: raised when the user inputs interrupt keys (Ctrl + C or Delete).
  8. RuntimeError: occurs when an error does not fall into any category. 
  9. NameError: raised when a variable is not found in the local or global scope. 
  10. MemoryError: raised when programs run out of memory. 
  11. ValueError: occurs when the operation or function receives an argument with the right type but the wrong value. 
  12. ZeroDivisionError: raised when you divide a value or variable with zero. 
  13. SyntaxError: raised by the parser when the Python syntax is wrong. 
  14. IndentationError: occurs when there is a wrong indentation.
  15. SystemError: raised when the interpreter detects an internal error.

You can find a complete list of errors and exceptions in Python by reading the documentation

Learn about Python exceptions by taking our Object-Oriented Programming in Python course. It will teach you how to create classes and leverage inheritance and polymorphism to reuse and optimize code.

Exception Handling with try, except, else, and finally

After learning about errors and exceptions, we will learn to handle them by using try, except, else, and finally blocks. 

So, what do we mean by handling them? In normal circumstances, these errors will stop the code execution and display the error message. To create stable systems, we need to anticipate these errors and come up with alternative solutions or warning messages. 

In this section, we will learn what each block does and how we can use them to write robust code. 

try and except Statement 

The most simple way of handling exceptions in Python is by using the `try` and `except` block.  

  1. Run the code under the `try` statement.
  2. When an exception is raised, execute the code under the `except` statement. 

Instead of stopping at error or exception, our code will move on to alternative solutions. 

try except statement in Python

Simple example

In the first example, we will try to print the undefined `x` variable. In normal circumstances, it should throw the error and stop the execution, but with the `try` and `except` block, we can change the behavior of the flow. 

  • The program will run the code under the `try` statement. 
  • As we know that `x` is not defined, so it will run the except statement and print the warning.
try:
   print(x)
except:
   print("An exception has occurred!")
An exception has occurred!

Multiple except statement example

In the second example, we will be using multiple `except` statements for handling multiple types of exceptions.

  •  If a ZeroDivisionError exception is raised, the program will print "You cannot divide a value with zero."
  • For the rest of the exceptions, it will print “Something else went wrong.”

It allows us to write flexible code that can handle multiple exceptions at a time without breaking. 

try:
   print(1/0)
except ZeroDivisionError:
   print("You cannot divide a value with zero")
except:
   print("Something else went wrong")
You cannot divide a value with zero

Loading the file example

Now, let’s look at a more practical example. 

In the code below, we are reading the CSV file, and when it raises the FileNotFoundError exception, the code will print the error and an additional message about the `data.csv` file. 

Yes, we can print default error messages without breaking the execution. 

try:
   with open('data.csv') as file:
       read_data = file.read()
except FileNotFoundError as fnf_error:
   print(fnf_error)
   print("Explanation: We cannot load the 'data.csv' file")
[Errno 2] No such file or directory: 'data.csv'
Explanation: We cannot load the 'data.csv' file

try with else Clause

We have learned about `try` and `except`, and now we will be learning about the `else` statement.

When the `try` statement does not raise an exception, code enters into the `else` block. It is the remedy or a fallback option when you expect a part of your script will produce an exception. It is generally used in a brief setup or verification section where you don't want certain errors to hide. 

Note: In the try-except block, you can use the `else` after all the `except` statements. 

try with else clause in Python

Simple example

We are adding the `else` statement to the ZeroDivisionError example. As we can see, when there are no exceptions, the print function under the `else` statement is executed, displaying the result. 

try:
   result = 1/3
except ZeroDivisionError as err:
   print(err)
else:
   print(f"Your answer is {result}")
Your answer is 0.3333333333333333

IndexError with else example

Let’s learn more by creating a simple function and testing it for various scenarios. 

The `find_nth_value` function has `x` (list) and `n` (index number) as arguments. We have created a try, except, and else block to handle the IndexError exception. 

x = [5,8,9,13]

def find_nth_value(x,n):
    try:
        result = x[n]
    except IndexError as err:
        print(err)
    else:
        print("Your answer is ", result)

The list `x` has four values and we will test it for the 6th index and 2nd index. 

# Testing
find_nth_value(x,6)
find_nth_value(x,2)
  • At n=6, the IndexError exception was raised, and we got to see the error default message “list index out of range.” 
  • At n=2, no exception was raised, and the function printed the result which is under the `else` statement. 
list index out of range
Your answer is  9

finally Keyword in Python

The `finally` keyword in the try-except block is always executed, irrespective of whether there is an exception or not. In simple words, the `finally` block of code is run after the try, except, the else block is final. It is quite useful in cleaning up resources and closing the object, especially closing the files.

finally keyword in Python

The `divide` function is created to handle ZeroDivisionError exceptions and display the result when there are no exceptions. No matter what the outcome is, it will always run `finally` to print “Code by DataCamp” in green color.

def divide(x,y):
    try:
        result = x/y
    except ZeroDivisionError:
        print("Please change 'y' argument to non-zero value")
    except:
        print("Something went wrong")
    else:
        print(f"Your answer is {result}")
    finally:
        print("\033[92m Code by DataCamp\033[00m")

In the first test, we are dividing 1 by 0, which should raise the ZeroDivisionError exception and print the message. As we can see, we have an additional line after the error message. 

divide(1,0)
Please change 'y' argument to non-zero value
 Code by DataCamp

When we add valid input, it displays the result by executing `else` and `finally` blocking.

divide(3,4)
Your answer is 0.75
 Code by DataCamp

Instead of an integer, we have added a string as a second argument which has raised an exception, which is different from ZeroDivisionError, with a different message. 

divide(1,'g')
Something went wrong
 Code by DataCamp

In all three scenarios, there is one thing in common. The code is always running the print function under the `finally` statement.

If you are new to Python and want to code like a real programmer, try our Python Programming skill track. You will learn to write efficient code, Python functions, software engineering, unit testing, and object-oriented programming. 

Nested Exception Handling in Python

We need nested exception handling when we are preparing the program to handle multiple exceptions in a sequence. For example, we can add another try-except block under the `else` statement. So, if the first statement does not raise an exception and check the second statement with the other half of the code. 

Modifying Divide Function

We have modified the `divide` function from the previous example and added a nested try-except block under the `else` statement. So, if there is no AttributeError, it will run the `else` and check the new code for the ZeroDivisionError exception.

def divide(x,y):
    
    try:
        value = 50
        x.append(value)
    
    except AttributeError as atr_err:
        print(atr_err)
        
    else:
        try:
            result = [i / y for i in x]
            print( result )
 except ZeroDivisionError:
            print("Please change 'y' argument to non-zero value")

    finally:
        print("\033[92m Code by DataCamp\033[00m")

In the first scenario, we are providing the list of 3 values `x` and denominator 3. The script will add 50 to the list and divide the individual value in the list by 3 and display the result. 

x = [40,65,70,87]
divide(x,3)

The function was successfully executed without raising any exceptions.

[13.333333333333334, 21.666666666666668, 23.333333333333332, 29.0, 16.666666666666668]
 Code by DataCamp

Instead of a list, we have provided an integer to the first argument, which has raised AttributeError.

divide(4,3)
'int' object has no attribute 'append'
 Code by DataCamp

In the last scenario, we have provided the list but 0 as the second argument that has raised the ZeroDivisionError exception under the `else` statement. 

divide(x,0)
Please change 'y' argument to non-zero value
 Code by DataCamp

File Editing Example

Let’s look at more practical examples of loading the file, writing a text, and closing the file. 

file_editor function will:

  • Check the FileNotFoundError exception for the `open()` function.
  • If the outer exception is not raised, it will check for the `write()` function exception.
  • No matter what, after opening the file, it will close the file by running the `finally` statement. 
  • If the outer try statement raises the exception, it will return the error message with an invalid file path. 
def file_editor(path,text):
    try:
      data = open(path)

      try:
        data.write(text)

      except:
        print("Unable to write the data. Please add an append: 'a' or write: 'w' parameter to the open() function.")

      finally:
        data.close()

    except:
      print(f"{path} file is not found!!")

In the first scenario, we are providing the file path and the text.

path = "data.txt"
text = "DataCamp Workspace: Share your data analysis in a cloud-based environment--no installation required."

file_editor(path,text)

The outer exception is raised. 

data.txt file is not found!!

To resolve the file not found exception, we must create a file “data.txt” using the Linux `echo` command. 

!echo "File by DataCamp" > "data.txt"

file editing in Workspace

After that, rerun the `file_editor()` function.

file_editor(path,text)

The inner exception is raised, as the `write` function is not able to add the text.

Unable to write the data. Please add an append: 'a' or write: 'w' parameter to the open() function.

To resolve this issue, we need to change the third line from `data = open(path)` to `data = open(path, 'a')`. It will allow us to append the new text to the file. 

After rerunning the function, we successfully added the text to the file. 

file_editor(path,text)

file editing screenshot in Workspace

Nested exception handling is not recommended as it makes exception handling more complex; instead, developers use multiple try-except blocks to create simple sequential exception handling. 

Note: you can also add a nested try-except block under the `try` or `except` statement. It just depends on your requirements. 

Raising Exceptions in Python

As a Python developer, you have the option to throw an exception if certain conditions are met. It allows you to interrupt the program based on your requirement. 

To throw an exception, we have to use the `raise` keyword followed by an exception name. 

Raising value error example

We can simply raise exceptions by adding a raise keyword in the if/else statement. 

In the example, we are raising the ValueError error if the value is greater than 1,000. We have changed the value to 2,000, which has made the `if` statement TRUE and raised ValueError with the custom message. The custom error message helps you figure out the problem quickly. 

value = 2_000
if value > 1_000:   
    # raise the ValueError
    raise ValueError("Please add a value lower than 1,000")
else:
    print("Congratulations! You are the winner!!")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)

----> 4     raise ValueError("Please add a value lower than 1,000")
      5 else:
      6     print("Congratulations! You are the winner!!")

ValueError: Please add a value lower than 1,000

Raising exception example

We can also raise any random built-in Python exception if the condition is met. In our case, we have raised a generic “Exception” with the error message.

if value > 1_000:   
    # raise the Exception
    raise Exception("Please add a value lower than 1,000")
else:
    print("Congratulations! You are the winner!!")
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
----> 3     raise Exception("Please add a value lower than 1,000")
      4 else:
      5     print("Congratulations! You are the winner!!")

Exception: Please add a value lower than 1,000

Handling raised exception example

We can also create our custom exception and handle the exception by using the try-except block. 

In the example, we have added a value error example under the `try` statement.  

So, how will it work? Instead of throwing the exception and terminating the program, it will display the error message that we have provided.

value = 2_000
try:
    if value > 1_000:
          
        # raise the ValueError
        raise ValueError("Please add a value lower than 1,000")
    else:
        print("Congratulations! You are the winner!!")
              
# if false then raise the value error
except ValueError as e:
        print(e)

This type of exception handling helps us prepare for the errors that are not covered by Python and are specific to your application requirement. 

Please add a value lower than 1,000

Conclusion

Both unit testing and exception handling are the core part of Python programming that makes your code production-ready and error-proof. In this tutorial, we have learned about exceptions and errors in Python and how to handle them. Furthermore, we have learned about complex nested try-except blocks and created custom exception blocks based on requirements.  

These tools and mechanisms are essential, but most of the work is done through simple `try` and `except` blocks. Where `try` looks for exceptions raised by the code and `except` handles those exceptions. 

If this is confusing and you don’t know where to start, complete our Python Data Science Toolbox (Part 1) course to understand scoping, Lambda functions, and error handling. You can also enroll in the Python Programmer career track to gain career-building skills and become a professional Python developer. 



Topics

Python Courses

Course

Introduction to Python

4 hr
5.4M
Master the basics of data analysis with Python in just four hours. This online course will introduce the Python interface and explore popular packages.
See DetailsRight Arrow
Start Course
See MoreRight Arrow
Related

Seaborn Heatmaps: A Guide to Data Visualization

Learn how to create eye-catching Seaborn heatmaps
Joleen Bothma's photo

Joleen Bothma

9 min

Test-Driven Development in Python: A Beginner's Guide

Dive into test-driven development (TDD) with our comprehensive Python tutorial. Learn how to write robust tests before coding with practical examples.
Amina Edmunds's photo

Amina Edmunds

7 min

Exponents in Python: A Comprehensive Guide for Beginners

Master exponents in Python using various methods, from built-in functions to powerful libraries like NumPy, and leverage them in real-world scenarios to gain a deeper understanding.
Satyam Tripathi's photo

Satyam Tripathi

9 min

Python Linked Lists: Tutorial With Examples

Learn everything you need to know about linked lists: when to use them, their types, and implementation in Python.
Natassha Selvaraj's photo

Natassha Selvaraj

9 min

A Beginner’s Guide to Data Cleaning in Python

Explore the principles of data cleaning in Python and discover the importance of preparing your data for analysis by addressing common issues such as missing values, outliers, duplicates, and inconsistencies.
Amberle McKee's photo

Amberle McKee

11 min

Python Data Classes: A Comprehensive Tutorial

A beginner-friendly tutorial on Python data classes and how to use them in practice
Bex Tuychiev's photo

Bex Tuychiev

9 min

See MoreSee More