This tutorial will teach you how to handle errors in Python effectively. You’ll learn about exceptions, try-except blocks, custom exceptions, and best practices for debugging and maintaining robust code.
Step 1: Understanding Python Exceptions
In Python, errors are represented as exceptions. When an error occurs, Python raises an exception, which can be caught and handled to prevent your program from crashing. Common built-in exceptions include ValueError, TypeError, IndexError, and KeyError.
Exceptions are objects that contain information about the error, such as its type and a message describing what went wrong.
print(10 / 0) # Raises ZeroDivisionErrorPractice Exercise
Write a program that attempts to divide two numbers. If the denominator is zero, catch the ZeroDivisionError and print a user-friendly message.
Show Solution
try:
numerator = 10
denominator = 0
result = numerator / denominator
print(result)
except ZeroDivisionError:
print('Error: Cannot divide by zero.')Step 2: Using Try-Except Blocks
The try-except block is used to catch and handle exceptions. Code inside the try block is executed, and if an exception occurs, the except block is executed instead of the program crashing.
You can also specify multiple except blocks to handle different types of exceptions.
try:
num = int(input('Enter a number: '))
print(10 / num)
except ValueError:
print('Invalid input. Please enter a number.')
except ZeroDivisionError:
print('Cannot divide by zero.')Practice Exercise
Write a program that asks the user for a file name and attempts to open it. Handle FileNotFoundError and PermissionError separately, providing appropriate messages for each.
Show Solution
try:
filename = input('Enter a file name: ')
with open(filename, 'r') as file:
print(file.read())
except FileNotFoundError:
print('Error: File not found.')
except PermissionError:
print('Error: Permission denied.')Step 3: The Else and Finally Clauses
The else clause in a try-except block runs if no exceptions occur. The finally clause runs no matter what, making it ideal for cleanup actions like closing files or releasing resources.
- else: Executes when the try block succeeds.
- finally: Executes regardless of whether an exception occurred.
try:
num = int(input('Enter a number: '))
result = 10 / num
except ZeroDivisionError:
print('Cannot divide by zero.')
else:
print(f'Result: {result}')
finally:
print('Execution complete.')Practice Exercise
Write a program that reads a list of numbers from a file and calculates their sum. Use finally to ensure the file is closed, even if an error occurs.
Show Solution
try:
with open('numbers.txt', 'r') as file:
numbers = [int(line) for line in file]
total = sum(numbers)
print(f'Sum: {total}')
except FileNotFoundError:
print('Error: File not found.')
except ValueError:
print('Error: Invalid data in file.')
finally:
print('File operation complete.')Step 4: Raising Exceptions
You can raise exceptions manually using the raise keyword. This is useful for enforcing constraints or signaling errors in your code.
You can raise built-in exceptions or custom exceptions (covered in the next step).
def validate_age(age):
if age < 0:
raise ValueError('Age cannot be negative.')
return age
try:
validate_age(-5)
except ValueError as e:
print(e)Practice Exercise
Write a function that checks if a password meets the following criteria: at least 8 characters long and contains at least one number. Raise a ValueError if the password is invalid.
Show Solution
def validate_password(password):
if len(password) < 8:
raise ValueError('Password must be at least 8 characters long.')
if not any(char.isdigit() for char in password):
raise ValueError('Password must contain at least one number.')
return True
try:
validate_password('weak')
except ValueError as e:
print(e)Step 5: Creating Custom Exceptions
Custom exceptions allow you to define your own error types, making your code more readable and maintainable. To create a custom exception, define a class that inherits from Python's Exception class.
class InvalidEmailError(Exception):
pass
def validate_email(email):
if '@' not in email:
raise InvalidEmailError('Invalid email address.')
return email
try:
validate_email('user.example.com')
except InvalidEmailError as e:
print(e)Practice Exercise
Create a custom exception called OutOfStockError. Write a function that checks if an item is in stock and raises this exception if it is not.
Show Solution
class OutOfStockError(Exception):
pass
def check_stock(item, stock):
if item not in stock:
raise OutOfStockError(f'{item} is out of stock.')
return True
try:
stock = ['apple', 'banana', 'orange']
check_stock('mango', stock)
except OutOfStockError as e:
print(e)Step 6: Logging Exceptions
Logging exceptions is crucial for debugging and monitoring applications. Python's logging module allows you to log errors with additional context, such as timestamps and severity levels.
Use logging.exception() within an except block to log the full traceback.
import logging
logging.basicConfig(filename='app.log', level=logging.ERROR)
try:
10 / 0
except ZeroDivisionError:
logging.exception('An error occurred:')Practice Exercise
Write a program that reads a JSON file and logs any JSONDecodeError or FileNotFoundError to a file called errors.log.
Show Solution
import logging
import json
logging.basicConfig(filename='errors.log', level=logging.ERROR)
try:
with open('data.json', 'r') as file:
data = json.load(file)
except FileNotFoundError:
logging.exception('File not found:')
except json.JSONDecodeError:
logging.exception('Invalid JSON format:')Step 7: Best Practices for Error Handling
- Be specific with exceptions: Catch only the exceptions you expect and handle them appropriately.
- Avoid bare except blocks: Catching all exceptions with a bare
except:can hide bugs. - Use logging: Log exceptions for debugging and monitoring.
- Clean up resources: Use
finallyor context managers to ensure resources are released. - Document exceptions: Clearly document the exceptions your functions can raise.
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
logging.error('Attempted to divide by zero.')
raise
# Example usage
try:
divide(10, 0)
except ZeroDivisionError:
print('Handled division by zero.')Practice Exercise
Refactor the following code to follow best practices: catch only specific exceptions, log errors, and ensure resources are cleaned up.
try:
file = open('data.txt', 'r')
data = file.read()
print(data)
except:
print('An error occurred.')
finally:
file.close()Show Solution
import logging
logging.basicConfig(filename='app.log', level=logging.ERROR)
try:
with open('data.txt', 'r') as file:
data = file.read()
print(data)
except FileNotFoundError:
logging.exception('File not found:')
except PermissionError:
logging.exception('Permission denied:')
except Exception as e:
logging.exception(f'Unexpected error: {e}')Sign in to take Cornell notes on this lesson — they save automatically and stay with your account.