Python Try-Except

Master Python error handling with the try-except construct. Safeguard your code against crashes by anticipating potential errors. Handle exceptions gracefully, providing informative error messages to users and preventing program termination. This approach leads to more robust and user-friendly Python applications.

Python-Try-Except
Table of Contents

Python try-except Basics

Python’s try-except block helps you handle errors gracefully. It’s like having a safety net in your code to catch unexpected problems. 

Syntax of Python try-except

try:
except ExceptionType:

  • try: This block contains the code you think might raise an error.
  • except ExceptionType: This part catches the error (if it happens).
    • ExceptionType: Here, you specify the type of error you want to catch (or you can use a general Exception to catch any error).

Example of Python try-except

# Function to divide two numbers
def divide(numerator, denominator):
    try:
        result = numerator / denominator  # This might cause an error
    except ZeroDivisionError:  # Catch the ZeroDivisionError exception
        print("Oops! Can't divide by zero.")
    else:  # Optional block that executes if no exception happens
        print("Result:", result)
 
# Call the divide function with a zero denominator
divide(10, 0)

Explanation

  • Lines 2: Define a function divide that takes two numbers as arguments.
  • Lines 3-4: The try block attempts the division.
  • Line 5: The except ZeroDivisionError block catches the specific error ZeroDivisionError that might occur if the denominator is zero.
  • Line 6: If a ZeroDivisionError happens, this code is executed, printing an informative message.
  • Lines 7-8: The else block (optional) will only run if no exception occurs. Here, it prints the result of the division.
  • Line 11: Calls the divide function with 10 and 0, which will trigger the except block due to the zero division.

Output

Oops! Can’t divide by zero.


Catching Exceptions in Python

The core of Python’s try-except structure lies in catching exceptions – those errors that pop up unexpectedly while your code runs. The try block is where the action happens; the except block is your safety net. You can even specify the exact type of exception you want to catch, like a TypeError when wrong data types are used or a FileNotFoundError if an essential file is missing. By catching exceptions, you prevent the program from crashing and instead have the opportunity to handle the situation gracefully, maybe even prompting the user to correct their input.

Specific Exceptions

You can catch specific errors using the except clause in the Python try-except block. This allows you to tailor your error handling to the issue that might arise.

Syntax

try:
except SpecificErrorType:

Line 2: This part defines the exception you want to catch (e.g., ZeroDivisionError, NameError).

Example

# Function to divide and greet
def divide_and_greet(name, age):
    try:
        # Potential division by zero and name lookup error
        result = 100 / age
        message = f"Hello, {name.upper()}! You are {(result // 10)} decades old."
    except ZeroDivisionError:
        print("Sorry, cannot divide by zero.")
    except AttributeError:
        print("Name must be a string.")

# Call the function with a zero age and a non-string name (list)
divide_and_greet("Alice", 0)
divide_and_greet(["Bob"], 30)

Explanation

  • Lines 1-2: Define a function divide_and_greet that takes a name and age.
  • Lines 2-5: The try block attempts the division and creates a greeting message.
  • Line 6: The first except block catches ZeroDivisionError if the age is zero.
  • Line 7: If a ZeroDivisionError occurs, this prints an error message specific to that exception.
  • Line 8: The second except block catches AttributeError if the name is not a string (like a list in this example).
  • Line 9: If an AttributeError occurs, this prints a different error message.
  • Lines 10-11: Call the function with different inputs to trigger both exception cases.

Output

Sorry, cannot divide by zero.
Name must be a string.

Multiple Exceptions

Python’s try-except block allows you to handle multiple exceptions using a single except clause. This is beneficial when you expect different errors that might require similar handling.

Syntax

try:
except (ExceptionType1, ExceptionType2, …):

Line 2: You can list multiple exception types within parentheses, separated by commas. The block will execute if any of those exceptions occur.

Example

# Function to get user input and divide
def divide_and_check(value):
    try:
        # Potential division by zero and value error
        result = 100 / value
        if result < 0:
            raise ValueError("Result is negative")  # Simulate a value error
        message = f"The result is {result}."
    except (ZeroDivisionError, ValueError) as e:  # Catch both exceptions
        message = f"Error: {e}"  # Generic message for either exception
    return message

# Get user input
user_value = float(input("Enter a number: "))

# Call the function with the input
result_message = divide_and_check(user_value)
print(result_message)

Explanation

  • Line 2: Define a function divide_and_check that takes a value as input.
  • Lines 3-8: The try block attempts the division and checks for a negative result (which we simulate as a ValueError).
  • Line 9: A single except clause using parentheses groups ZeroDivisionError and ValueError together. If either of these exceptions occurs, the following block executes.
  • Line 10: The as e part assigns the exception object to the variable e. Here, we create a generic error message using the exception type stored in e.
  • Line 11: The function returns the message containing either the result or the error message.
  • Lines 14-17: Get user input, convert it to a float, and call the function with the input.
  • Line 18: Print the returned message, which will display the result or error depending on what happened in the try block.

Wildcard Exception: Catch-All

While Python lets you catch any exception with an unqualified except:, it’s often a risky move. This broad catch-all might mask specific errors, making debugging harder.

Syntax

try:
except:

Line 2: Without specifying an exception type, this catches any error.

Example

# Function to read a file and process its contents
def process_file(filename):
    try:
        with open(filename, "r") as f:
            data = int(f.read().strip())
            result = 100 / data
    except:  # Catches EVERYTHING, including potential errors in calculation
        print("A generic error occurred. Could not process the file.")

# Call the function with a non-existent file and trigger errors
process_file("missing_file.txt") 
process_file("data.txt")  # Contains "Hello" which can't be converted to int

Explanation

  • Lines 2-7: Define a function process_file that tries to open a file, read data, convert it to an integer, and perform a calculation. It uses an unqualified except clause that catches all kinds of errors.
  • Line 8: A generic error message is printed if any exception happens, masking the specific error.
  • Lines 11-12: The function is called twice – once with a non-existent filename (triggering a FileNotFoundError) and then with a file containing invalid integer data (triggering a ValueError). The generic error message makes it hard to pinpoint the exact problem.

Output

A generic error occurred. Could not process the file.
A generic error occurred. Could not process the file.


Using the Exception Instance (for Error Details)

The except clause can capture more than just the exception type. It can also store the exception instance, giving you access to detailed error information. This can be helpful for crafting more informative error messages or handling errors in more specific ways.

Syntax

try:
except ExceptionType as e:

as e: This assigns the exception instance to a variable (e in this case). You can use this variable to access information about the error.

Example

# Function to convert a string to a number
def convert_to_number(num_str):
    try:
        return float(num_str)
    except ValueError as e:
        print(f"Error converting '{num_str}': {e}")  # Access error information

# Try converting a valid and invalid string
converted_value = convert_to_number("123.4")
convert_to_number("abc")

Explanation

  • Lines 2-5: Define a function convert_to_number that tries to convert a string to a float.
  • Line 5: The except ValueError as e clause catches ValueError and assigns the exception instance to e.
  • Line 6: Inside the except block, you can access the exception information using the variable e. Here, it prints the invalid string along with the error message from the exception object.
  • Lines 9-10: The function is called with a valid and an invalid string.
    • Line 9: Conversion is successful, and no exception occurs.
    • Line 10: Conversion fails due to invalid characters, triggering the except block. The error message now includes the specific invalid string that caused the error.

Output

Error converting ‘abc’: could not convert string to float: ‘abc’


else Clause

The else clause in Python try-except block acts like a safety zone. It’s a block of code that only executes if there are no exceptions in the try block. Think of it as a place to put your happy code that runs smoothly when everything works as expected.

Syntax

try:
except:
else:

Example

# Function to read a file and print its contents
def read_file(filename):
    try:
        with open(filename, "r") as f:
            contents = f.read()
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found.")
    else:
        print(f"Contents of {filename}:")
        print(contents)

# Try reading existing and non-existing files
read_file("my_file.txt")
read_file("missing_file.txt")

Explanation

  • Lines 2-5: Define a function read_file that attempts to open a file for reading.
  • Lines 3-6: The try block tries to open the file and read its contents.
  • Lines 6-7: The except FileNotFoundError block handles cases where the file is not found.
  • Lines 8-10: The else block only executes if the try block completes successfully without exceptions. Here, it prints the contents of the file.
  • Lines 13-14: The function is called with an existing file and a non-existent file.
    • Line 13: The file is read successfully, and the else block prints the contents.
    • Line 14: The FileNotFoundError exception is triggered due to the missing file, and the else block is skipped.

Output

Error: File ‘my_file.txt’ not found.
Error: File ‘missing_file.txt’ not found.


finally Clause

The finally clause in Python try-except block is your clean-up area. It’s a block of code that executes no matter what happens in the try block, whether there’s an exception or smooth execution. This is useful for releasing resources like closing files or database connections, ensuring they’re properly handled even if errors occur.

Syntax

try:
except:
finally:

Example

# Function to read a file (closing it in finally)
def read_file_safe(filename):
    try:
        with open(filename, "r") as f:  # Context manager handles opening/closing
            contents = f.read()
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found.")
    finally:
        print("File operation complete.")  # Always executes

# Try reading existing and non-existing files
read_file_safe("my_file.txt")
read_file_safe("missing_file.txt")

Explanation

  • Lines 2-5: Define a function read_file_safe that uses a with open statement as a context manager to open the file. The file object is assigned to f.
  • Line 4: The context manager automatically closes the file when the indented block ends (normally or due to an exception).
  • Line 5: The file contents are read.
  • Lines 6-7: The except block handles the FileNotFoundError.
  • Lines 8-9: The finally block executes regardless of exceptions. Here, it prints a message to indicate file operation completion.
  • Lines 12-13: The function is called with existing and non-existent files.
    • In both cases, the finally block ensures the with open context manager closes the file (if opened) before the function returns.

Output

Error: File ‘my_file.txt’ not found.
File operation complete.
Error: File ‘missing_file.txt’ not found.
File operation complete.


Combining else and finally

In Python’s try-except block, you can combine the else and finally clauses for a more robust approach. The else clause executes code only when there are no exceptions in the try block, and the finally clause always runs at the end, regardless of exceptions.

Syntax

try: ..
except: ..
else: ..
finally:

Example

# Function to check and print file contents
def check_and_print_file(filename):
    try:
        with open(filename, "r") as f:
            contents = f.read()
            return contents  # Return contents if successful
    except FileNotFoundError:
        return None  # Indicate file not found
    else:
        print(f"File '{filename}' exists and contents retrieved successfully.")
    finally:
        print("File check completed.")  # Always executes

# Try checking existing and non-existing files
file_content = check_and_print_file("my_file.txt")
if file_content is not None:
    print(file_content)  # Print contents if available
check_and_print_file("missing_file.txt")

Explanation

  • Lines 2-5: Define a function check_and_print_file that tries to open a file for reading. It uses a with open statement to manage the file.
  • Line 5: The file contents are read and stored in contents.
  • Line 6: If successful, the function returns the contents.
  • Lines 7-8: The except block handles FileNotFoundError by returning None to indicate the file wasn’t found.
  • Lines 9-10: The else block executes only if there’s no exception (file found). It prints a success message.
  • Lines 11-12: The finally block always executes. Here, it prints a message indicating the file check is complete.
  • Lines 15-17: The function checks an existing file (“my_file.txt”).
    • The returned value (contents) is assigned to file_content.
    • An if statement checks if file_content is not None (indicating successful retrieval). If so, the contents are printed.
  • Line 18: The function is called again with a non-existent file (“missing_file.txt”).
    • The except block is triggered due to the missing file and None is returned.
    • The else block is skipped.
    • The finally block still executes and prints its message.

Output

File check completed.
File check completed.


Nested try-except Blocks

In Python try-except block, you can have try-except structures nested inside each other. This is useful when handling exceptions at different levels of your code. Imagine it like having multiple safety nets! The inner try-except handles errors specific to that code block, while the outer try-except can catch more general errors.

Syntax

try: … # Outer try block
   inner_try: … # Inner try block
   except InnerExceptionType: … # Handle inner exceptions
except OuterExceptionType: … # Handle outer exceptions

Example

# Function to process data from a file
def process_file_data(filename):
    try:
        with open(filename, "r") as f:
            data = f.read()
            try:  # Nested try block for data conversion
                data_list = [float(x) for x in data.split()]
            except ValueError:
                print(f"Error: Invalid data format in '{filename}'.")
            else:
                # Calculate and print average (assuming valid data)
                average = sum(data_list) / len(data_list)
                print(f"Average of numbers in '{filename}': {average}")
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found.")

# Try processing data from existing and non-existent files
process_file_data("data.txt")  # data.txt contains [1, 2, 3]
process_file_data("missing_file.txt")

Explanation

  • Lines 2-5: Define a function process_file_data that tries to open a file for reading.
  • Lines 3-6: The outer try block handles potential file-related errors.
  • Lines 4-5: The file is opened using a with open statement.
  • Lines 6-10: The inner try block attempts to convert the file data to a list of floats (assuming valid data).
  • Lines 7-8: The inner except block catches ValueError if the conversion fails due to invalid data formats in the file.
  • Lines 9-10: If the inner conversion is successful, the else block calculates and prints the average of the numbers.
  • Line 14: The outer except block handles FileNotFoundError if the file is not found.
  • Lines 18-19: The function is called with an existing file (“data.txt”) and a non-existent file (“missing_file.txt”).
    • In the first case, the data is processed successfully, and the average is printed.
    • In the second case, the outer except block catches the FileNotFoundError.

Output

Error: File ‘data.txt’ not found.
Error: File ‘missing_file.txt’ not found.


Raising Exceptions

In Python, you can deliberately create exceptions using the raise keyword. This helps signal errors in your code’s logic or when you want to prevent the program from continuing due to an invalid condition. 

Syntax

raise ExceptionType[, message]

  • ExceptionType: This specifies the type of exception to raise (e.g., ValueError, ZeroDivisionError). You can also create custom exception classes.
  • message (optional): You can optionally provide a custom error message to make the exception more informative.

Example

# Function to get a positive integer from the user
def get_positive_integer():
    while True:  # Loop until valid input is entered
        user_input = input("Enter a positive integer: ")
        try:
            value = int(user_input)
            if value <= 0:
                raise ValueError("Please enter a positive integer.")  # Raise an error
            return value  # Exit loop if valid input
        except ValueError as e:
            print(f"Error: {e}")  # Print the error message

# Get a positive integer from the user
positive_value = get_positive_integer()
print(f"You entered: {positive_value}")

Explanation

  • Lines 2-4: Define a function get_positive_integer that uses a loop to keep prompting the user until they enter a valid positive integer.
  • Lines 5-10: The try block attempts to convert the user input to an integer and then checks if it’s positive.
  • Line 6: Integer conversion is attempted.
  • Lines 7-8: If the value is not positive, a ValueError is raised with a custom message explaining the error.
  • Line 9: The function returns the value if the input is valid (positive integer).
  • Line 10: The except block catches the ValueError raised within the try block and prints the error message.
  • Lines 11-15: The function is called to get a positive integer from the user. The loop continues prompting until a valid input is entered.

Custom Exceptions

Python allows you to create your exception classes to handle specific errors in your application. This makes your code more readable and maintainable. You can define custom exceptions by inheriting from the built-in Exception class.

Syntax

class MyCustomException(Exception): # Inherits from Exception
   … # Custom logic or attributes for your exception

Example

# Function to check and process a file
def check_and_process_file(filename):
    try:
        with open(filename, "r") as f:
            data = f.read()
            if not data.isdigit():  # Check if data contains only digits
                raise InvalidFileFormatException("File contains non-numeric data.")
            # Process the numeric data (assuming valid format)
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found.")
    except InvalidFileFormatException as e:
        print(f"Error: {e}")  # Print the custom error message

# Class for invalid file format exception
class InvalidFileFormatException(Exception):
    def __init__(self, message):
        self.message = message  # Store the error message

# Try processing existing files with valid and invalid data formats
check_and_process_file("data.txt")  # data.txt contains "123"
check_and_process_file("invalid_data.txt")  # invalid_data.txt contains "abc"

Explanation

  • Lines 15-17: Define a custom exception class InvalidFileFormatException that inherits from Exception. It has an __init__ method to store an error message.
  • Lines 2-8: Define a function check_and_process_file that tries to open a file.
  • Lines 3-6: The try block handles potential file access errors.
  • Line 7: If the file data contains non-numeric characters, a InvalidFileFormatException is raised with a specific error message.
  • Lines 9-10: The except block handles FileNotFoundError.
  • Line 11: A separate except block catches the custom InvalidFileFormatException.
  • Line 12: The custom error message from the exception is printed.
  • Lines 20-21: The function is called with files having valid and invalid data formats.
    • In the first case, the data is processed successfully (assuming valid numeric data).
    • In the second case, the custom InvalidFileFormatException is raised due to non-numeric data.

Output

Error: File ‘data.txt’ not found.
Error: File ‘invalid_data.txt’ not found.


How to Print the Exception Name

You can easily print the name (type) of the occurred exception. This can be helpful for debugging purposes, as it gives you a quick idea of the error you’re facing.

Syntax

try:
except ExceptionType as e: # Capture exception in variable e
   print(f”An error occurred: {type(e)}”) # Print exception type using type(e)

Example

# Function to divide two numbers
def divide(num1, num2):
    try:
        result = num1 / num2
    except ZeroDivisionError as e:  # Catch ZeroDivisionError
        print(f"Error: {type(e)}. Cannot divide by zero.")  # Print exception type
    else:
        print(f"Result: {result}")

# Try dividing by zero and a valid number
divide(10, 2)
divide(10, 0)

Explanation

  • Lines 2-5: Define a function divide that attempts to divide two numbers.
  • Line 5: The except ZeroDivisionError as e clause catches a specific exception (ZeroDivisionError) and assigns it to the variable e.
  • Line 6: Inside the except block, you can use type(e) to get the type of the exception, which is the exception class itself (e.g., ZeroDivisionError in this case). This is then printed along with a message.
  • Lines 7-8: The else block (optional) executes only if there’s no exception.
  • Lines 11-12: The function is called with valid and invalid inputs (division by zero).
    • In the first case, the division is successful, and the else block prints the result.
    • In the second case, the ZeroDivisionError is triggered, and the exception type is printed.

Output

Result: 5.0
Error: <class 'ZeroDivisionError'>. Cannot divide by zero.

Best Practices for Python try-except Blocks

Here are some best practices for using try-except blocks in Python:

  • Catch specific exceptions: Avoid broad, unqualified except clauses, as they might mask more specific errors.
  • Provide informative error messages: Include enough context in error messages for effective debugging.
  • Handle errors gracefully: Recover from errors when possible or guide the user to correct their input.
  • Don’t suppress all errors: Only catch exceptions you intend to handle, allowing others to propagate.
  • Log exceptions for later analysis: Log critical exceptions for troubleshooting and error analysis.
  • Clean up resources in finally: Use the finally block to ensure resources are released properly, even in the event of an exception.
  • Create custom exceptions: Define custom exception classes for specific error conditions in your application.
  • Raise exceptions strategically: Use raise to signal errors or prevent invalid program states.
  • Test exception handling: Include unit tests to verify your try-except blocks function as expected.

Common Built-in Python Exceptions

Comprehensive list of common built-in exceptions in Python. Note that more specialized exceptions exist depending on modules and libraries you’re using.

Base Exceptions

  • Exception: The root base class from which all exceptions inherit.
  • SystemExit: Raised by the sys.exit() function to exit the interpreter.
  • KeyboardInterrupt: Raised when the user interrupts the program (Ctrl+C).
  • GeneratorExit: Raised when a generator or coroutine is closed.
  • StopIteration: Raised when a built-in next() function or iterator has no more items.

Standard Error Types

  • ArithmeticError: Base class for arithmetic errors.
    • ZeroDivisionError: Division by zero.
    • OverflowError: Arithmetic result exceeds numerical limits.
    • FloatingPointError: Error in floating-point operations.
  • AssertionError: When an assert statement fails.
  • AttributeError: Attribute reference or assignment fails.
  • EOFError: Raised at end-of-file (no more data to read).
  • ImportError: When an import (import statement) fails.
    • ModuleNotFoundError: A module required for import is not found.
  • IndexError: Sequence index is out of range.
  • KeyError: Key is not found in a dictionary.
  • LookupError: Base class for errors in lookup operations.
    • IndexError: Lookup index is out of range.
    • KeyError: Lookup key not found.
  • MemoryError: Raised when there’s an issue with memory allocation.
  • NameError: A name is not found.
    • UnboundLocalError: A local variable is referenced before assignment.
  • NotImplementedError: Method or feature is not implemented (common in abstract classes).
  • OSError: System-related error (accessing a file, I/O).
    • FileNotFoundError: File or directory not found.
    • BlockingIOError: Operation (e.g., a read) would block and the underlying resource is in non-blocking mode.
  • RuntimeError: Error occurred during program execution.
  • SyntaxError: Errors in Python syntax.
  • SystemError: Internal system error.
  • TypeError: An operation or function is applied to an object of the wrong type.
  • ValueError: Function argument has an inappropriate value.
    • UnicodeError: Unicode encoding or decoding error.