DEV Community

Cover image for How to Implement Error Handling in Python with Try-Except Blocks
Praise Idowu
Praise Idowu

Posted on

How to Implement Error Handling in Python with Try-Except Blocks

Errors can be both annoying and frustrating. Often, these exceptions occur due to incorrect implementation or usage.

As programmers, it is crucial to anticipate exceptions and handle them correctly. Throughout this lesson, we will explore the most common exceptions encountered and use practical examples to explain the fundamentals of error handling.

Prerequisites

  • A working knowledge of Python programming
  • Python installed on your system
  • A code editor
  • A willingness to learn and experiment with Python error-handling

Basics of Try-Except

The try-except block is used to handle exceptions. Let us take a look at the try and except blocks and explore how they work.

Try block

The try block is where you enclose the code that you anticipate might raise an error.

Except block

This block handles the exception.

Try-Except

try:
    # Code that may raise an error
except:
    # Handle the exception, e.g., print an error message
Enter fullscreen mode Exit fullscreen mode

Handling different types of exceptions

A try block can have several exceptions to handle specific errors. You could also use the Exception block to generalize every error.

try:
   pass
except Exception:
    # do something
    pass
Enter fullscreen mode Exit fullscreen mode

There are also cases where you want to print the error message instead of a different message in the output.

try:
    result = '5' + 2
    print(result)
except TypeError as e:
    print('error', e)

Enter fullscreen mode Exit fullscreen mode

Result in the terminal:
error can only concatenate str (not "int") to str

How do you know what specific error to handle?
Simply run the code without an error handler; whatever error it throws at the terminal, you want to use it in the except block.

Several errors you could encounter are:

  • Syntax error
  • NameError
  • IndexError
  • KeyError
  • ZeroDivisionError
  • TypeError
  • ValueError
  • ImportError
  • FileNotFoundError
  • OsError
  • AttributeError

If you want to read more about these errors check the documentation.

Using specific exception types

Step 1: Concatenating a string and an integer without a try-except block.
print('a' + 5)

Result in the Terminal:
print('a' + 5)
TypeError: can only concatenate str (not "int") to str

Step 2: Now that we have identified the exception, let’s use a try-except block.

try:
    result = 'a' + 5
    print(result)
except TypeError:  
     print('invalid operation')
Enter fullscreen mode Exit fullscreen mode

Result in the terminal: invalid operation

Using multiple except blocks

In this example, we have a try block that contains code that may raise exceptions. We have multiple exception blocks to handle specific exception types.

try:
    # Code that may raise exceptions
    num = int(input("Enter a number: "))
    result = 10 / num
    fruits = ["apple", "banana", "cherry"]
    index = int(input("Enter an index to access a fruit: "))
    print(f"The selected fruit is: {fruits[index]}")
except ValueError:
    # Handle a ValueError (e.g., when user input can't be converted to int)
    print("Invalid input. Please enter a valid number.")
except ZeroDivisionError:
    # Handle a ZeroDivisionError (e.g., when dividing by zero)
    print("Division by zero is not allowed.")
except IndexError:
    # Handle an IndexError (e.g., when accessing an index outside the list range)
    print("Invalid index. Please choose a valid index.")
except Exception as e:
    # Handle other exceptions not explicitly caught above
    print(f"An error occurred: {e}")

Enter fullscreen mode Exit fullscreen mode

The else block

The else block is useful for executing code when no exceptions occur, providing a way to separate the normal code flow from exception handling logic.

try:
    # Code that may or may not raise an exception
    num1 = int(input("Enter the first number: "))
    num2 = int(input("Enter the second number: "))
    result = num1 / num2
except ZeroDivisionError:
    # Handle division by zero exception
    print("Division by zero is not allowed.")
except ValueError:
    # Handle invalid input (non-integer) exception
    print("Invalid input. Please enter valid numbers.")
else:
    # Code to execute if no exceptions are raised
    print(f"The result of the division is: {result}")

Enter fullscreen mode Exit fullscreen mode

Using the 'finally' block for cleanup

Regardless of whether an exception is raised or not, the code within the finally block is executed.
This can be used for closing files or network connections.

try:
    # Code that may or may not raise an exception
    file = open("example.txt", "r")
    data = file.read()
except FileNotFoundError:
    # Handle file not found exception
    print("File not found.")
except Exception as e:
    # Handle other exceptions
    print(f"An error occurred: {e}")
finally:
    # Code that always executes, regardless of exceptions
    print("Cleaning up resources...")
    file.close()  # Close the file to release resources
Enter fullscreen mode Exit fullscreen mode

Custom Exception Handling

Raising exceptions with the 'raise' keyword

Raising exceptions is important for signaling errors and exceptional conditions in your code.

def some_function():
    # Some condition that warrants raising an exception
    raise Exception("This is a custom exception message.")

try:
    some_function()
except Exception as e:
    print(f"Exception caught: {e}")
Enter fullscreen mode Exit fullscreen mode

Example: Imagine you are writing a program that calculates the square root of a number provided by the user. You want to ensure that the user enters a valid positive number because the square root of negative numbers is undefined in the real number system.

import math

def calculate_square_root(number):
    if number < 0:
        raise ValueError("Invalid input: Please enter a positive number.")
    return math.sqrt(number)

try:
    user_input = float(input("Enter a number: "))
    result = calculate_square_root(user_input)
    print(f"The square root of {user_input} is {result}")
except ValueError as e:
    print(f"Error: {e}")
Enter fullscreen mode Exit fullscreen mode

Result 1:
Enter a number: -2
Error: Invalid input: Please enter a positive number.
Result 2:
Enter a number: re
Error: could not convert string to float: 're'

Exercise

In programming you can only familiarize yourself with these concepts through practice, look things up online, and try things out. For the exceptions that weren’t covered in this article, purposely create some issues. For example, if it were a syntax error you could write a function without a colon or maybe a variable without the equal sign. For NameError you could print out a variable name that doesn’t exist. In the case of an importError, you could try importing a module that doesn’t exist.

Mistakes to avoid when handling exceptions

  • The error message should be specific and easily understood.
try:
    user_input = int(input('Enter a number: '))
except ValueError as e:
   print('An error has occurred')
Enter fullscreen mode Exit fullscreen mode

Why is it a problem?
What error has occurred? This message is confusing to both the user and the developer. Imagine this was a program with several thousands of lines of code, and you are trying to debug. With this kind of message, it makes debugging harder. Picture a user using this program. They will feel frustrated because the error message doesn’t explain clearly what they did wrong.

Better way to write it:

try:
    user_input = int(input('Enter a number: '))
except ValueError as e:
    print('Enter a valid integer')
    print(e)
Enter fullscreen mode Exit fullscreen mode
  • Relying on exceptions to control program flow. For example, using exceptions for if-else logic.
try:
    result = num / 2
    if num <= 0:
        raise ValueError(f'{num} must be greater than zero')
    print(result)
except ValueError as e:
    print(e)
Enter fullscreen mode Exit fullscreen mode

Why is it a problem?
A simple if-else block would have solved it. Always remember that simplicity is preferable to writing complex code.

Better way to write it:

num = 5

if num > 0:
    print(num / 2)
else:
    print(f'{num} must be greater than zero')
Enter fullscreen mode Exit fullscreen mode
  • Placing large sections of code within a try block.
try:
    num1 = int(input("Enter the first number: "))
    num2 = int(input("Enter the second number: "))
    result = num1 / num2
    with open("sample1.txt", "r") as file:
        content = file.read()
except Exception as e:
    print(f"An error occurred: {e}")
Enter fullscreen mode Exit fullscreen mode

Why is it a problem?
You got a user input and performed a division which is okay. Opening a file within the try block is wrong. A better approach would be to remove the code opening a file.

Conclusion

We can draw the following conclusion from this lesson:

  • Handling exceptions prevents your program from crashing unexpectedly.
  • We can use multiple except blocks.
  • The error message should be easily understood.
  • Raise exceptions wisely.

If you enjoyed reading this, you just need to do this. Follow me on Twitter and LinkedIn.

Top comments (0)