Handle Exceptions with try except in Python

PythonBeginner
Practice Now

Introduction

In this lab, you will learn how to effectively handle exceptions in Python using the try...except statement. We will explore how to catch specific exceptions like ValueError, handle multiple exception types, and use the else and finally blocks for more control over your program's flow. You will also learn how to raise custom exceptions to signal specific error conditions in your code. Through hands-on exercises, you will gain practical experience in writing robust and error-tolerant Python programs.

This is a Guided Lab, which provides step-by-step instructions to help you learn and practice. Follow the instructions carefully to complete each step and gain hands-on experience. Historical data shows that this is a beginner level lab with a 100% completion rate. It has received a 100% positive review rate from learners.

Handle ValueError with try except

In this step, you will learn how to handle a ValueError using the try...except statement. A ValueError occurs when a function receives an argument of the correct type but an inappropriate value. A common example is trying to convert a string that is not a number into an integer using the int() function.

We will write a Python script that prompts the user for an integer. If the input is not a valid integer, we will catch the ValueError and display a user-friendly message.

In the WebIDE file explorer on the left, find and open the handle_value_error.py file located in the ~/project directory. Then, add the following code to it:

while True:
    try:
        x = int(input('Please enter an integer: '))
        print(f'You entered: {x}')
        break
    except ValueError:
        print('That was not a valid integer. Please try again.')

Save the file.

Now, open the integrated terminal and run the script using the python command:

python ~/project/handle_value_error.py

The script will prompt you to enter an integer. First, try entering a non-integer value like hello to see the error handling in action. Then, enter a valid integer like 123 to see the successful execution path.

Example output:

Please enter an integer: hello
That was not a valid integer. Please try again.
Please enter an integer: 123
You entered: 123

In this code:

  • The while True: loop ensures the program keeps asking for input until a valid integer is entered.
  • The try block contains the code that might raise an exception, specifically the int(input(...)) call.
  • If a ValueError occurs in the try block, the code inside the except ValueError: block is executed.
  • If no exception occurs, the print() statement runs, and break exits the loop.

This demonstrates how try...except ValueError allows you to gracefully handle invalid input without crashing the program.

Handle Multiple Exceptions

A block of code can potentially raise different types of exceptions. Python allows you to handle multiple exceptions using several except clauses or by grouping exceptions in a single except clause.

Let's write a script that performs division. This operation can raise a ValueError if the input is not a number and a ZeroDivisionError if the divisor is zero.

In the WebIDE, open the file ~/project/handle_multiple_exceptions.py and add the following code. This version uses separate except blocks for each error type.

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    print(f"Result: {result}")
except ValueError:
    print("Invalid input. Please enter integers only.")
except ZeroDivisionError:
    print("Error: Division by zero is not allowed.")

Save the file and run it from the terminal:

python ~/project/handle_multiple_exceptions.py

Test the script with different inputs to trigger each exception:

  1. Enter a non-integer value (e.g., abc).
  2. Enter 0 for the denominator.
  3. Enter valid integers for both.

Example output for division by zero:

Enter the numerator: 10
Enter the denominator: 0
Error: Division by zero is not allowed.

You can also handle multiple exceptions with a single except clause by grouping them in a tuple. This is useful for performing the same action for different errors.

Now, update the code in ~/project/handle_multiple_exceptions.py to use this grouped approach:

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    print(f"Result: {result}")
except (ValueError, ZeroDivisionError):
    print("An error occurred: Invalid input or division by zero.")

Save the file and run it again with the same test cases to observe the new, combined error message.

Execute Code with the else Clause

The try statement can include an optional else clause. The code in the else block is executed only if the try block completes without raising any exceptions. This is useful for separating the code that should run on success from the main try block.

Let's modify our division script to include an else block.

In the WebIDE, open the file ~/project/try_except_else.py and add the following code:

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
except (ValueError, ZeroDivisionError):
    print("An error occurred: Invalid input or division by zero.")
else:
    print("Division successful!")
    print(f"Result: {result}")

Save the file and run it from the terminal:

python ~/project/try_except_else.py

Test the script with both failing and successful inputs:

  1. Enter 0 for the denominator to trigger an exception.
  2. Enter valid, non-zero integers to execute the else block.

Example outputs:

Enter the numerator: 10
Enter the denominator: 0
An error occurred: Invalid input or division by zero.
Enter the numerator: 10
Enter the denominator: 2
Division successful!
Result: 5.0

As you can see, the messages in the else block are only printed when the division is successful. If an exception occurs, the except block is executed, and the else block is skipped.

Ensure Code Execution with the finally Clause

The try statement also has an optional finally clause. The code inside the finally block is always executed, regardless of whether an exception occurred in the try block or not. This makes it ideal for cleanup actions, such as closing files or releasing resources, that must happen in all scenarios.

Let's add a finally block to our division example.

In the WebIDE, open the file ~/project/try_except_finally.py and add the following code:

try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    print(f"Result: {result}")
except (ValueError, ZeroDivisionError):
    print("An error occurred: Invalid input or division by zero.")
finally:
    print("Execution finished.")

Save the file and run it from the terminal:

python ~/project/try_except_finally.py

Run the script with both failing and successful inputs:

  1. Enter 0 for the denominator to trigger an exception.
  2. Enter valid, non-zero integers.

Example outputs:

Enter the numerator: 10
Enter the denominator: 0
An error occurred: Invalid input or division by zero.
Execution finished.
Enter the numerator: 10
Enter the denominator: 2
Result: 5.0
Execution finished.

Notice that the message "Execution finished." from the finally block is printed in both cases. The finally block is guaranteed to execute, making it a reliable place for essential cleanup code.

Raise Custom Exceptions

Sometimes you need to signal an error condition that isn't a built-in Python exception. You can do this with the raise statement, which allows you to create and trigger your own exceptions. This is useful for making your application's error handling more specific and descriptive.

First, let's see how to raise a built-in exception. In the WebIDE, open the file ~/project/raise_exception.py and add the following code:

def check_positive(number):
    if number <= 0:
        raise ValueError("Input must be a positive number")
    print(f"The number {number} is positive.")

try:
    check_positive(-5)
except ValueError as e:
    print(f"Caught an exception: {e}")

try:
    check_positive(10)
except ValueError as e:
    print(f"Caught an exception: {e}")

Save the file and run it from the terminal:

python ~/project/raise_exception.py

The output will be:

Caught an exception: Input must be a positive number
The number 10 is positive.

Here, the check_positive function raises a ValueError if the input is not positive, which is then caught by the except block.

Now, let's define and raise a custom exception. Custom exceptions are classes that inherit from the built-in Exception class.

In the WebIDE, open the file ~/project/custom_exception.py and add the following code:

class NegativeNumberError(Exception):
    """Custom exception raised for negative numbers."""
    pass

def process_positive_number(number):
    if number < 0:
        raise NegativeNumberError("Negative numbers are not allowed")
    print(f"Processing positive number: {number}")

try:
    process_positive_number(-10)
except NegativeNumberError as e:
    print(f"Caught custom exception: {e}")

try:
    process_positive_number(20)
except NegativeNumberError as e:
    print(f"Caught custom exception: {e}")

Save the file and run it from the terminal:

python ~/project/custom_exception.py

The output will be:

Caught custom exception: Negative numbers are not allowed
Processing positive number: 20

In this example, we defined our own NegativeNumberError and raised it under a specific condition. The try...except block then specifically catches this custom error type, making the error handling more precise.

Summary

In this lab, you learned how to implement robust error handling in Python. You started by using a try...except block to catch a specific ValueError from user input. You then expanded on this by handling multiple exception types, both separately and in a single block. You also learned to use the else clause to run code only when no exceptions occur and the finally clause to execute cleanup code in all situations. Finally, you practiced creating and raising custom exceptions to handle application-specific errors, making your code more readable and maintainable.