7 of 20 topics completed
Python Error Handling and Exceptions
Even the most carefully written programs can encounter unexpected situations. Python's exception handling system helps you deal with errors gracefully, making your programs more robust and user-friendly. In this tutorial, you'll learn how to catch and handle errors effectively.
💡 Why Error Handling Matters
Imagine you've created a calculator app that divides two numbers. What happens if a user tries to divide by zero? Without proper error handling, your program would crash! With error handling, you can display a helpful message and allow the user to try again.
Understanding Errors and Exceptions
In Python, there are two main types of errors:
Syntax Errors
These occur when Python can't understand your code because it violates the language rules.
# Syntax error exampleif x == 5 # Missing colonprint("x is 5")
Python will point out syntax errors before your program runs.
Exceptions
These occur during execution when something unexpected happens, even if the syntax is correct.
# Exception examplex = 10 / 0 # ZeroDivisionErrorprint(y) # NameError - y is not defined
Exceptions happen while your program is running.
Common Python Exceptions
Python has many built-in exception types. Here are some you'll encounter frequently:
Exception | Description | Example |
---|---|---|
ZeroDivisionError | Dividing by zero | 10 / 0 |
TypeError | Operation on an inappropriate type | "2" + 2 |
ValueError | Operation on a value with the right type but inappropriate value | int("abc") |
NameError | Using a variable that doesn't exist | print(undefined_var) |
IndexError | Accessing an index that's out of range | my_list = [1, 2]; my_list[5] |
KeyError | Accessing a dictionary key that doesn't exist | my_dict = ; my_dict["key"] |
FileNotFoundError | Trying to open a file that doesn't exist | open("missing.txt") |
Basic Exception Handling with try/except
The try
/except
block is the foundation of error handling in Python:
1# Basic structure2try:3 # Code that might cause an exception4 result = 10 / 05except:6 # Code that runs if an exception occurs7 print("An error occurred!")
Let's look at a more practical example:
1def divide_numbers(a, b):2 try:3 result = a / b4 return result5 except:6 print("Error: Division by zero is not allowed!")7 return None89# Test the function10print(divide_numbers(10, 2)) # Output: 5.011print(divide_numbers(10, 0)) # Output: Error: Division by zero is not allowed!12 # None
⚠️ Best Practice
Avoid using a bare except:
clause as it will catch all exceptions, including ones you might not expect. This can make debugging harder. Instead, specify which exceptions you want to catch.
Catching Specific Exceptions
It's better to catch specific exceptions rather than using a blanket except
clause:
1def divide_numbers_better(a, b):2 try:3 result = a / b4 return result5 except ZeroDivisionError:6 print("Error: Division by zero is not allowed!")7 return None8 except TypeError:9 print("Error: Please provide numbers!")10 return None1112# Test with different inputs13print(divide_numbers_better(10, 2)) # Output: 5.014print(divide_numbers_better(10, 0)) # Output: Error: Division by zero is not allowed!15print(divide_numbers_better(10, "2")) # Output: Error: Please provide numbers!
Handling Multiple Exceptions
You can handle multiple exceptions in different ways:
Multiple except Blocks
try:# Code that might raise exceptionsnum = int(input("Enter a number: "))result = 100 / numprint(f"100 divided by {num} is {result}")except ValueError:print("That's not a valid number!")except ZeroDivisionError:print("You can't divide by zero!")
Grouping Exceptions
try:# Code that might raise exceptionsnum = int(input("Enter a number: "))result = 100 / numprint(f"100 divided by {num} is {result}")except (ValueError, ZeroDivisionError):print("Please enter a non-zero number!")
Getting Exception Information
You can capture the actual exception object to get more information about what went wrong:
1try:2 # Try to open a file that doesn't exist3 with open("nonexistent_file.txt", "r") as file:4 content = file.read()5except FileNotFoundError as error:6 print(f"Error: {error}")7 # Output: Error: [Errno 2] No such file or directory: 'nonexistent_file.txt'89 # You can access attributes of the error object10 print(f"Error code: {error.errno}")11 print(f"Error message: {error.strerror}")12 print(f"Filename: {error.filename}")
The else and finally Clauses
You can extend try
/except
with else
and finally
clauses:

1try:2 # This block might raise an exception3 number = int(input("Enter a positive number: "))4 if number <= 0:5 raise ValueError("That's not a positive number!")6except ValueError as e:7 # This block runs if an exception occurs8 print(f"Error: {e}")9else:10 # This block runs only if NO exception occurs11 print(f"You entered {number}, which is positive!")12finally:13 # This block ALWAYS runs, regardless of whether an exception occurred14 print("Thank you for using our program!")
try
Contains code that might cause an exception.
except
Runs if an exception occurs in the try block.
else
Runs if the try block completes with no exceptions.
finally
Always runs, regardless of whether an exception occurred. Useful for cleanup actions.
Raising Exceptions
Sometimes you want to trigger exceptions yourself when certain conditions aren't met:
1def set_age(age):2 if not isinstance(age, int):3 raise TypeError("Age must be an integer")45 if age < 0 or age > 150:6 raise ValueError("Age must be between 0 and 150")78 print(f"Age set to {age}")910# Test the function11try:12 set_age(25) # Works fine13 set_age(-5) # Raises ValueError14except ValueError as e:15 print(f"Error: {e}")1617try:18 set_age("twenty") # Raises TypeError19except TypeError as e:20 print(f"Error: {e}")
Creating Custom Exceptions
You can create your own exception types by inheriting from existing exceptions:
1class InvalidAgeError(Exception):2 """Exception raised for invalid age values."""3 pass45class InvalidEmailError(Exception):6 """Exception raised for invalid email format."""7 def __init__(self, email, message="Invalid email format"):8 self.email = email9 self.message = message10 super().__init__(self.message)1112 def __str__(self):13 return f"{self.message}: {self.email}"1415# Using custom exceptions16def register_user(name, age, email):17 if age < 13:18 raise InvalidAgeError(f"User must be at least 13 years old. Got: {age}")1920 if "@" not in email or "." not in email:21 raise InvalidEmailError(email)2223 print(f"User {name} registered successfully!")2425# Test with different inputs26try:27 register_user("Alice", 25, "alice@example.com") # Valid input28 register_user("Bob", 10, "bob@example.com") # Invalid age29except InvalidAgeError as e:30 print(f"Age error: {e}")31except InvalidEmailError as e:32 print(f"Email error: {e}")
Practical Error Handling: User Input Validation
Let's see a practical example of error handling for user input:
1def get_integer_input(prompt, min_value=None, max_value=None):2 """3 Get an integer input from the user with validation.45 Args:6 prompt: The message to display to the user7 min_value: Optional minimum allowed value8 max_value: Optional maximum allowed value910 Returns:11 An integer within the specified range12 """13 while True:14 try:15 value = int(input(prompt))1617 if min_value is not None and value < min_value:18 print(f"Error: Value must be at least {min_value}")19 continue2021 if max_value is not None and value > max_value:22 print(f"Error: Value must be at most {max_value}")23 continue2425 return value # Valid input, exit the loop2627 except ValueError:28 print("Error: Please enter a valid integer")2930# Using the function31age = get_integer_input("Enter your age (18-120): ", 18, 120)32print(f"You entered: {age}")3334# Example interaction:35# Enter your age (18-120): abc36# Error: Please enter a valid integer37# Enter your age (18-120): 1538# Error: Value must be at least 1839# Enter your age (18-120): 13040# Error: Value must be at most 12041# Enter your age (18-120): 2542# You entered: 25
Advanced Pattern: Context Managers
The with
statement (used with files) is based on Python's context manager protocol, which provides automatic resource management and error handling:
1# File handling with context manager2with open("example.txt", "w") as file:3 file.write("Hello, world!")4 # File is automatically closed when the block ends, even if an exception occurs56# Creating a custom context manager7class DatabaseConnection:8 def __init__(self, connection_string):9 self.connection_string = connection_string10 self.connection = None1112 def __enter__(self):13 print(f"Connecting to database: {self.connection_string}")14 # In a real app, this would be: self.connection = db.connect(self.connection_string)15 self.connection = "DB CONNECTION"16 return self.connection1718 def __exit__(self, exc_type, exc_val, exc_tb):19 print("Closing database connection")20 # In a real app: self.connection.close()2122 # If we return True, any exception will be suppressed23 if exc_type is not None:24 print(f"An error occurred: {exc_val}")25 return True # Suppress the exception2627# Using our custom context manager28try:29 with DatabaseConnection("postgresql://localhost/mydb") as conn:30 print("Connected!")31 # Do database operations...3233 # Let's cause an error34 if True:35 raise ValueError("Something went wrong!")3637 print("This line won't execute")38except ValueError:39 print("This exception was suppressed by the context manager")4041print("Program continues...")
🎯 Try it yourself!
Create a function that reads a JSON file and safely handles these potential errors:
- File not found
- File contains invalid JSON
- Missing expected fields in the JSON data
Best Practices for Error Handling
- Be specific - Catch only the exceptions you expect and can handle.
- Fail early, fail loudly - Detect and report errors as soon as possible.
- Don't catch what you can't handle - Let exceptions you can't properly handle propagate up.
- Keep the try block small - Only wrap code that might cause the exception.
- Use finally for cleanup - Ensure resources are properly closed or released.
- Provide helpful error messages - Users should understand what went wrong.
- Log exceptions - In larger applications, log exceptions for debugging.
Summary
In this tutorial, you've learned:
- How to use
try
/except
to handle exceptions - Catching specific exception types
- Using
else
andfinally
clauses - Raising exceptions when conditions aren't met
- Creating custom exception classes
- Best practices for effective error handling
Effective error handling makes your programs more reliable and user-friendly. Rather than crashing when something unexpected happens, your program can respond gracefully and continue running or exit cleanly.
Related Tutorials
Learn how to work with files in Python.
Learn moreMaster creating and using functions in Python.
Learn moreLearn about if statements, loops, and control flow in Python.
Learn more