How to Catch Multiple Exceptions in Python

In Python, handling multiple exceptions is a technique utilized to address and manage various types of errors and debugging situations that may arise during code execution. In this discussion, we will elucidate the mechanism for catching multiple exceptions and highlight our observations. A solid understanding of Python's exception handling is essential for a deeper exploration of this subject.

Python will raise an exception when your program encounters a rare yet anticipated problem, such as attempting to access a file that cannot be found. It is advisable to implement code that can appropriately manage these exceptions.

An issue arises in the code when it performs in a manner that is nonsensical, such as executing a calculation incorrectly. These errors must be identified and eliminated. This procedure is referred to as Debugging, and it is essential for the functionality of the software.

When a Python program encounters an error and triggers an exception, it may result in the program crashing. However, prior to this occurrence, Python generates a Traceback message that informs us about the issue. Below is a straightforward example:

Example -

Example

Python 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)] on win32

Type "help", "copyright", "credits" or "license" for more information.

>>> 10/"One"

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: unsupported operand type(s) for /: 'int' and 'str'

The expression 10 / "One" triggers a TypeError, displaying the message "unsupported operand type(s) for /: 'int' and 'str'". In this situation, Python attempts to execute the division operation; however, it is not feasible to divide an integer by a string.

When an operation is attempted with a string, it results in a TypeError due to the operation being unsupported. To manage exceptions and avoid your program from terminating unexpectedly when errors take place, you can utilize the try statement in Python. This statement allows you to observe your code for any exceptions and implement suitable responses should they occur. Such a practice contributes to a more seamless and user-friendly experience.

Example -

Example

try:

    first_input = input("Enter first number ")

    second_input = input("Enter second number ")

    # Attempt to convert user inputs to float

    first = float(first_input)

    second = float(second_input)

    # Check for division by zero

    if second == 0:

        raise ZeroDivisionError

    result = first / second

    print(f"{first} divided by {second} is {result}")

except ValueError:

    print("Please enter valid numbers")

except ZeroDivisionError:

    print("You can't divide by zero")

except Exception as e:

    print(f"An unexpected error occurred: {e}")

Output:

Output

Enter first number 32

1Enter second number 16

32.0 divided by 16.0 is 2.0

Enter first number 23

Enter second number 0

You can't divide by zero

Enter first number 12

Enter second number "five"

Please enter valid numbers

In the code provided above, we initially obtain user input in the form of strings and subsequently strive to convert these strings into float values within the try block. Furthermore, we explicitly verify for division by zero prior to executing the division operation. Moreover, we have included a general except block designed to manage any unforeseen errors that may arise during the execution of the code.

Handling Multiple Python Exceptions

Managing distinct exceptions using individual except blocks is appropriate when distinct responses are necessary for each specific exception type. On the other hand, if you observe that the same responses are applied to several exceptions, you can enhance code clarity and readability by consolidating multiple exceptions into a unified except block. This can be accomplished by listing the exceptions as a tuple in the except statement.

Next, we will modify the preceding code to manage the exception using a single line. Let’s examine the example below.

Example -

Example

try:

    first = float(input("Enter first number: "))

    second = float(input("Enter second number: "))

    print(f"{first} divided by {second} is {first / second}")

except (ValueError, ZeroDivisionError):

    print("Oops! You can't divide the number by 0")

Output:

In Case 1, the output will be:

Example

Enter first number: 12

Enter second number: 6

12.0 divided by 6.0 is 2.0

In case 2, the output will be:

Example

Enter first number: 5

Enter second number: 0

Oops! You can't divide the number by 0

In the above example, we can use a single except clause to handle both ValueError and ZeroDivisionError exceptions. Similarly, we can also include additional except clauses to handle other exceptions if that's needed, just in case.

During the first test case, only the try block executes since no exceptions are encountered. In the subsequent second and third test scenarios, we deliberately cause a ValueError and a ZeroDivisionError, respectively. We have successfully observed both exceptions being handled through the same except clause. In both instances, the program adheres to the sequence of try followed by except (which addresses either ValueError or ZeroDivisionError). As demonstrated, the exception handler effectively manages both types of errors without disruption.

Identifying the Captured Python Exception

Within the realm of object-oriented programming, classes serve as blueprints that outline the properties and functionalities of the objects that are instantiated. Whenever an exception occurs in your Python code, it generates an object based on the class that specifies that particular exception. For example, when a ValueError exception is triggered, you are essentially instantiating an object of the ValueError class.

It is not necessary to possess extensive expertise in object-oriented programming to manage exceptions; however, understanding that various categories of exceptions originate from different classes can be advantageous. To put it another way, exceptions can be likened to an assortment of tools within a toolbox, where each tool is designed for a specific purpose when an issue arises in your code.

Example -

Example

try:

    first = float(input("Enter first number? "))

    second = float(input("Enter second number? "))

    print(f"{first} divided by {second} is {first / second}")

except (ValueError, ZeroDivisionError):

    print(f"A {type(error).__name__} has occurred.")

Output:

Output

Enter first number: 10

Enter second number: 2

10.0 divided by 2.0 is 5.0

Enter first number: 23

Enter second number: "two"

A ValueError has occurred.

Enter first number: 10

Enter second number: 0

A ZeroDivisionError has occurred.

Imagine you aim to enhance your code by incorporating functionality for multiplying numbers. Additionally, you decide to manually trigger a RuntimeError if the user engages in unsupported or unforeseen actions, such as simply pressing the Enter key.

Let's understand the following example -

Example -

Example

from operator import mul, truediv

def calculate(operator, operand1, operand2):

    return operator(operand1, operand2)

try:

    first = float(input("Enter your first number: "))

    second = float(input("Enter your second number: "))

    operation = input("Enter either * for multiplication or / for division: ")

    if operation == "*":

        answer = calculate(mul, first, second)

    elif operation == "/":

        if second == 0:

            raise ZeroDivisionError("Division by zero is not allowed")

        answer = calculate(truediv, first, second)

    else:

        raise ValueError("Unsupported operation")

except (ValueError, ZeroDivisionError, RuntimeError) as error:

    print(f"An error occurred: {error}")

    if isinstance(error, ValueError):

        print("Make sure you entered valid numbers.")

    elif isinstance(error, ZeroDivisionError):

        print("You can't divide by zero.")

    elif isinstance(error, RuntimeError):

        print("Unsupported operation.")

else:

    print(f"{first} {operation} {second} = {answer}")

Output:

Output

Enter your first number: 10

Enter your second number: "h"

An error occurred: could not convert string to float: '"h"'

Enter a valid numbers.

In this revised iteration, we have incorporated the mul and truediv functions from the operator module to execute multiplication and division tasks. These functions are supplied as parameters to the calculate function, accompanied by two numerical values. Within the calculate function, the relevant operator function, selected from either mul or truediv, is invoked based on the specified operation. This mechanism effectively carries out the calculation; however, it relies on the input of two numerical values and the specification of either '*' for multiplication or '/' for division.

When a user inputs an operator that is not supported, your program deliberately raises a RuntimeError. If this error is not managed correctly, it may lead to a crash of the application.

In the except segment, we define a tuple containing the exceptions that we want to catch, and you assign the exception that has been caught to a variable named error. At the outset, the code outputs the name of the class of the exception, regardless of which exception occurs. In a similar vein, a match block is utilized to show a tailored message according to the specific exception being addressed. This approach allows you to offer pertinent error messages to the user in relation to the nature of the error that has been encountered.

Handling Multiple Possible Python Exceptions Using a Superclass

In Python, multiple exceptions are represented as instances of distinct classes, all of which belong to the framework of the Python exception class hierarchy. Every exception in Python originates from a class named BaseException, and within this structure, there is a parent class referred to as the Exception class. The BaseException class acts as the foundational class for all exceptions.

In the realm of exceptions, inheritance fundamentally entails structuring exceptions in a hierarchical manner. For example, it is evident that ArithmeticError serves as a subclass of the broader exception category. The differences among these classes are quite subtle.

The OSError class has two derived subclasses: PermissionError and FileNotFoundError. This indicates that both PermissionError and FileNotFoundError inherit from OSError. Furthermore, they are also classified as exception subclasses, given that OSError is a descendant of the Exception class.

Example

Example

#importing sterror

from os import strerror

try:

    with open("datafile.txt", mode="rt") as f:

        print(f.readlines())

except OSError as error:

    print(strerror(error.errno))

Explanation:

The provided code is designed to display the contents of a file called datafile.txt; however, it will function properly only if the file is present. In the event that datafile.txt cannot be located, the code will trigger a FileNotFoundError. Although there is a single except block intended to catch OSError, it is also capable of managing FileNotFoundError since this exception is a subclass of OSError.

In order to identify the particular subclass of OSError that has occurred, we can utilize the type(error).name method to display its class name. Nevertheless, this may not be relevant for the majority of users. As another option, you can observe the underlying error by examining the .errno attribute. This attribute holds a numerical code produced by the operating system, which provides valuable information regarding the nature of the problem that caused the OSError.

Handle Multiple Python Exceptions Using contextlib.suppress

In certain scenarios, the code might encounter different exceptions that we need to bypass to maintain its operational integrity. The conventional method for managing exceptions in Python involves capturing them without executing any particular response. If we adhere to this method, the code would appear as follows:

Example

Example

try:

    with open("file.txt", mode="rt") as f:

        print(f.readlines())

except (FileNotFoundError, PermissionError):

    pass

Explanation:

To manage the FileNotFoundError that could arise when the specified file is absent, we typically catch the exception and employ the "pass" statement within the exception handler, which effectively disregards it. Executing our code under these circumstances will yield no output, and crucially, it ensures that our program does not terminate abruptly:

Employing this method in our code can lead to ambiguity for the reader. Essentially, we are instructing the program to intercept an exception and then completely disregard it.

Example

Example

#importing suppress from the context library

from contextlib import suppress

with suppress(FileNotFoundError, PermissionError):

    with open("file.txt", mode="rt") as f:

        print(f.readlines())

Explanation:

In the provided code, the suppress function is utilized to establish a context manager intended to manage both FileNotFoundError and PermissionError exceptions. The with statement subsequently applies this context manager to the enclosed code. If a different kind of exception were to be raised, the code would continue to operate properly without resulting in a crash. Nevertheless, it is crucial to understand that if any code were positioned outside of the "with" block, the exception handling would not be applicable, rendering it ineffective.

Consider an alternative situation: Picture having several files labeled "transactions.txt," each generated at various intervals during the day. These files hold information that we aim to consolidate into one comprehensive file called "alltransactions.txt." Once the merging process is complete, the code proceeds to archive the original file as "transactions.archts," where "ts" denotes a timestamp to ensure its uniqueness. Nonetheless, there are instances when our application attempts to locate a file that may not exist at all.

Example

Example

#importing the pathlib library

import pathlib

#importing the time library

import time

#from the contextlib module, importing the suppress library

from contextlib import suppress

# Define the paths to the temporary and main files

temporary_file = pathlib.Path("transactions.txt")

main_file = pathlib.Path("all_transactions.txt")

while True:

    # Creating a unique archive path with a timestamp

    archive_path = temporary_file.with_suffix(f".arch_{time.time()}")

    # Use suppress to handle FileNotFoundError without any problem

    with suppress(FileNotFoundError):

        # It opens the temporary file for reading

        with temporary_file.open(mode="rt") as transactions:

            # It opens the main file for appending

            with main_file.open(mode="at") as main:

                print("Found new transactions, updating log, & archiving")

                # Read lines from the temporary file and append them to the main file

                main.writelines(transactions.readlines())

        # Replacing the temporary file with the archived version

        temporary_file.replace(archive_path)

    # It pauses for 3 seconds before the next iteration

    time.sleep(3)

Explanation

This script continuously monitors the "transactions.txt" file for any new entries, subsequently adding those entries to the "all_transactions.txt" file, and then archives the original file by adding a timestamp to its name. It employs the suppress context manager to gracefully manage FileNotFoundError, ensuring that the absence of the temporary file does not cause any issues.

Catch Multiple Python Exceptions Using Exception Groups

When implementing a try-except block within the code, it is capable of intercepting the first exception that arises inside the try block. Should we attempt to initiate multiple exceptions, the program will cease execution after addressing the initial exception, and any subsequent similar exceptions will not be triggered. A practical illustration of this can be observed in the following code.

To gain a clearer understanding of the aforementioned statement, let's examine an example:

Example:

Example

#Here is the List of exceptions

exceptions = [ZeroDivisionError(), FileNotFoundError(), NameError()]

# Initializing error count variables

n_zd_errors = n_fnf_errors = n_name_errors = 0

try:

    # Here we are iterating through the exceptions list and raise each exception

    for e in exceptions:

        raise e

except ZeroDivisionError:

    n_zd_errors += 1

except FileNotFoundError:

    n_fnf_errors += 1

except NameError:

    n_name_errors += 1

finally:

    # Printing the counts of each type of exception raised

    print(f"ZeroDivisionError occurred {n_zd_errors} times.")

    print(f"FileNotFoundError occurred {n_fnf_errors} times.")

    print(f"NameError occurred {n_name_errors} times.")

Output:

Output

ZeroDivisionError occurred 1 times.

FileNotFoundError occurred 0 times.

NameError occurred 0 times.

In the code presented above, three variables identified as ZeroDivisionError, FileNotFoundError, and NameError are all set to an initial value of zero.

Within the try block, a loop goes through the list of exceptions and triggers each exception. Nevertheless, the code is designed to catch and process only one exception at any given moment.

Within the except block, distinct clauses are designated for handling ZeroDivisionError, FileNotFoundError, and NameError. Ultimately, a finally block is executed to display the total counts of each category of exception that was raised.

Conclusion

This tutorial explores the techniques for capturing multiple exceptions in Python. We will discuss multiple exception handling, a feature in Python that allows developers to effectively manage and address various error types and debugging issues that arise during code execution.

We emphasized the significance of error management to avoid program failures, and the try and except statements for exception handling demonstrated an effective approach to managing multiple exceptions. Adequate exception handling guarantees the dependability of the code while providing user-friendly error notifications, thereby improving the overall efficiency and resilience of the program.

How to Catch Multiple Exceptions in Python FAQs

1. Why do we need to catch multiple exceptions?

Occasionally, a segment of code might produce multiple varieties of errors, and effectively managing these exceptions helps avert crashes while enabling us to react in different manners according to the specific type of exception encountered.

2. How do we catch multiple exceptions in Python?

The tuple syntax can be utilized within a single except block. To illustrate this concept, let's examine the following example:

Example

try:

    x = int("xyz")   # ValueError

    y = 10 / 0       # ZeroDivisionError

except (ValueError, ZeroDivisionError) as e:

    print(f"Error: {e}")

3. Can we handle multiple exceptions with separate except blocks?

Indeed, it is possible to manage several exceptions simultaneously, as each except block can address distinct categories of errors. Let’s illustrate this with an example:

Example

try:

    num = int("xyz")

    result = 10 / 0

except ValueError:

    print("Invalid conversion to integer.")

except ZeroDivisionError:

    print("Division by zero is not allowed.")

4. Can we catch all exceptions together?

Indeed, we have the capability to handle all exceptions simultaneously by utilizing the syntax except Exception as e, which is recognized as the foundational class for the majority of exceptions.

Example

Try:

    faulty_code()

except Exception as e:

    print("Something went wrong:", e)

5. Can I ignore an exception without handling it?

Indeed, we can bypass an exception without explicitly managing it by utilizing the pass statement.

Example

try:

    faulty_code()

except (ValueError, ZeroDivisionError):

    pass  # It will ignore the exception or the error silently

6. Can we use a variable name when catching multiple exceptions?

Indeed, when handling multiple exceptions, we can utilize a variable name by employing the as keyword.

Example:

Example

try:

    lst = [1, 2, 3]

    print(lst[4])   # IndexError

except (ValueError, IndexError) as error:

    print("Caught:", error)

Output:

Output

Caught: list index out of range

7. Can we use else and finally along with multiple except blocks?

Indeed, it is possible to utilize both else and finally in conjunction with multiple blocks. Let’s explore this concept through an example:

Example:

Example

try:

    n = int("10")

#using except

except ValueError:

    print("Invalid input")

#using else

else:

    print("No exception has  occurred")

#using finally

finally:

    print("Always executed")

Output:

Output

No exception has occurred

Always executed

Input Required

This code uses input(). Please provide values below: