Exception Basics: Your Safety Net
Welcome to Exception Basics! Think of exceptions as warning signals that something unexpected happened. Instead of crashing your program, Python raises an exception that you can catch and handle.
What Are Exceptions?
Exceptions are Pythonβs way of saying βHey, something went wrong!β Theyβre like emergency brakes that stop your program before it causes more damage.
Exceptions vs Syntax Errors
# Syntax Error - caught during parsing
print("Hello World" # Missing parenthesis - SYNTAX ERROR
# Exception - occurs during execution
print(10 / 0) # ZeroDivisionError - EXCEPTION
Basic Try-Except Blocks
The Basic Structure
try:
# Code that might cause an exception
risky_code()
except ExceptionType:
# Code to handle the exception
handle_error()
Simple Example
# Without error handling - crashes
number = int(input("Enter a number: "))
result = 100 / number
print(f"Result: {result}")
# With error handling - graceful
try:
number = int(input("Enter a number: "))
result = 100 / number
print(f"Result: {result}")
except ZeroDivisionError:
print("Cannot divide by zero!")
except ValueError:
print("Please enter a valid number!")
Common Exception Types
ValueError - Invalid Data Type
# Converting invalid string to int
try:
age = int("twenty-five") # Can't convert text to number
except ValueError:
print("Please enter a valid number for age")
age = 0
ZeroDivisionError - Division by Zero
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
result = 0
FileNotFoundError - Missing File
try:
with open("nonexistent.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("File not found, creating a new one")
with open("nonexistent.txt", "w") as file:
file.write("Default content")
IndexError - List Index Out of Range
numbers = [1, 2, 3]
try:
print(numbers[5]) # Index 5 doesn't exist
except IndexError:
print("Index out of range")
KeyError - Dictionary Key Not Found
person = {"name": "Alice", "age": 25}
try:
print(person["salary"]) # Key doesn't exist
except KeyError:
print("Salary information not available")
Multiple Exception Types
Handling Multiple Exceptions
def safe_divide(a, b):
try:
result = a / b
return result
except ZeroDivisionError:
print("Cannot divide by zero")
return 0
except TypeError:
print("Both arguments must be numbers")
return 0
except Exception as e:
print(f"An unexpected error occurred: {e}")
return 0
# Test the function
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # Cannot divide by zero, returns 0
print(safe_divide(10, "2")) # TypeError, returns 0
Catching Multiple Exceptions Together
try:
# Code that might raise different exceptions
value = int(input("Enter a number: "))
result = 100 / value
except (ValueError, ZeroDivisionError) as e:
print(f"Invalid input: {e}")
result = 0
The Else Clause
Using Else with Try-Except
def read_file_safely(filename):
try:
with open(filename, "r") as file:
content = file.read()
except FileNotFoundError:
print(f"File '{filename}' not found")
return None
else:
# Only executed if no exception occurred
print(f"Successfully read {len(content)} characters")
return content
# Test
content = read_file_safely("existing_file.txt") # Success
content = read_file_safely("missing_file.txt") # Error
The Finally Clause
Cleanup Code with Finally
def process_file(filename):
file = None
try:
file = open(filename, "r")
content = file.read()
# Process content...
print(f"Processed {len(content)} characters")
except FileNotFoundError:
print(f"File '{filename}' not found")
finally:
# Always executed, even if exception occurred
if file:
file.close()
print("File closed")
# Test
process_file("existing_file.txt") # File closed
process_file("missing_file.txt") # File closed (even though file was never opened)
Finally for Resource Management
def database_operation():
connection = None
try:
connection = connect_to_database()
# Perform database operations
result = connection.execute("SELECT * FROM users")
return result
except DatabaseError as e:
print(f"Database error: {e}")
return None
finally:
# Always close the connection
if connection:
connection.close()
print("Database connection closed")
Exception Objects
Accessing Exception Information
try:
numbers = [1, 2, 3]
print(numbers[10])
except IndexError as e:
print(f"Error type: {type(e).__name__}")
print(f"Error message: {e}")
print(f"Full exception: {repr(e)}")
Exception Hierarchy
# All exceptions inherit from BaseException
# Most user exceptions inherit from Exception
try:
# Some risky operation
pass
except Exception as e:
# Catches most exceptions (but not SystemExit, KeyboardInterrupt, etc.)
print(f"Caught exception: {e}")
except BaseException as e:
# Catches everything including system exits
print(f"Caught base exception: {e}")
Practical Examples
Example 1: Safe Calculator
def safe_calculator():
"""A calculator that handles errors gracefully."""
while True:
try:
# Get user input
expression = input("Enter calculation (or 'quit' to exit): ")
if expression.lower() == 'quit':
break
# Evaluate the expression
result = eval(expression)
print(f"Result: {result}")
except ZeroDivisionError:
print("Error: Cannot divide by zero")
except (ValueError, SyntaxError):
print("Error: Invalid mathematical expression")
except NameError:
print("Error: Unknown variable or function")
except Exception as e:
print(f"Unexpected error: {e}")
# Run the calculator
safe_calculator()
Example 2: Safe File Reader
def safe_read_file(filename, default_content=""):
"""Read a file safely with fallback content."""
try:
with open(filename, "r", encoding="utf-8") as file:
content = file.read()
print(f"Successfully read file: {filename}")
return content
except FileNotFoundError:
print(f"File '{filename}' not found, using default content")
return default_content
except PermissionError:
print(f"Permission denied reading '{filename}'")
return default_content
except UnicodeDecodeError:
print(f"Encoding error reading '{filename}'")
return default_content
except Exception as e:
print(f"Unexpected error reading file: {e}")
return default_content
# Test the function
content1 = safe_read_file("existing_file.txt")
content2 = safe_read_file("missing_file.txt", "Default text")
content3 = safe_read_file("readonly_file.txt")
Example 3: User Input Validation
def get_valid_age():
"""Get a valid age from user input."""
while True:
try:
age_str = input("Enter your age: ")
age = int(age_str)
if age < 0:
print("Age cannot be negative")
continue
elif age > 150:
print("Age seems too high")
continue
return age
except ValueError:
print("Please enter a valid number")
except KeyboardInterrupt:
print("\nOperation cancelled")
return None
def get_valid_email():
"""Get a valid email address."""
while True:
try:
email = input("Enter your email: ").strip()
if not email:
print("Email cannot be empty")
continue
elif "@" not in email:
print("Email must contain @ symbol")
continue
elif "." not in email.split("@")[1]:
print("Email must have a valid domain")
continue
return email
except KeyboardInterrupt:
print("\nOperation cancelled")
return None
# Test the functions
age = get_valid_age()
email = get_valid_email()
if age and email:
print(f"User is {age} years old with email: {email}")
Example 4: Configuration Loader
import json
import os
def load_config(filename="config.json"):
"""Load configuration with error handling."""
default_config = {
"debug": False,
"max_connections": 10,
"timeout": 30
}
if not os.path.exists(filename):
print(f"Config file '{filename}' not found, using defaults")
return default_config
try:
with open(filename, "r") as file:
config = json.load(file)
# Validate required fields
required_fields = ["debug", "max_connections", "timeout"]
for field in required_fields:
if field not in config:
print(f"Missing required field '{field}', using default")
config[field] = default_config[field]
print(f"Configuration loaded from '{filename}'")
return config
except json.JSONDecodeError as e:
print(f"Invalid JSON in config file: {e}")
print("Using default configuration")
return default_config
except PermissionError:
print(f"Permission denied reading '{filename}'")
return default_config
except Exception as e:
print(f"Unexpected error loading config: {e}")
return default_config
# Test
config = load_config("config.json")
print(f"Debug mode: {config['debug']}")
print(f"Max connections: {config['max_connections']}")
Best Practices
1. Be Specific with Exception Types
# β
Specific exception handling
try:
result = int(input("Number: ")) / int(input("Divisor: "))
except ValueError:
print("Invalid number")
except ZeroDivisionError:
print("Cannot divide by zero")
# β Too broad - catches everything
try:
result = int(input("Number: ")) / int(input("Divisor: "))
except Exception:
print("Something went wrong")
2. Use Finally for Cleanup
# β
Proper resource cleanup
file = None
try:
file = open("data.txt", "r")
data = file.read()
finally:
if file:
file.close()
3. Donβt Ignore Exceptions
# β Bad - silently ignores errors
try:
risky_operation()
except:
pass # Never do this!
# β
Good - handle or log the error
try:
risky_operation()
except Exception as e:
print(f"Error occurred: {e}")
# Or log to file, send notification, etc.
4. Use Context Managers
# β
Automatic resource management
with open("file.txt", "r") as file:
content = file.read()
# File automatically closed
# Equivalent to:
file = None
try:
file = open("file.txt", "r")
content = file.read()
finally:
if file:
file.close()
Practice Exercises
Exercise 1: Safe Division Calculator
Create a calculator that:
- Takes two numbers as input
- Performs division safely
- Handles all possible errors
- Shows appropriate error messages
Exercise 2: File Backup System
Build a file backup function that:
- Copies a file to backup location
- Handles file not found errors
- Handles permission errors
- Handles disk space errors
- Provides clear error messages
Exercise 3: User Registration System
Create a user registration system that:
- Validates username (length, characters)
- Validates email format
- Validates password strength
- Handles all validation errors gracefully
- Provides helpful error messages
Exercise 4: Data Processor
Build a data processing function that:
- Reads data from a file
- Processes the data (e.g., calculate averages)
- Handles file errors
- Handles data format errors
- Handles calculation errors
Exercise 5: Network Request Handler
Create a function that:
- Makes HTTP requests
- Handles connection errors
- Handles timeout errors
- Handles invalid URL errors
- Retries failed requests
Summary
Exception handling is your safety net:
Basic Structure:
try:
# Risky code
except SpecificException:
# Handle specific error
except Exception as e:
# Handle general errors
finally:
# Cleanup code (always runs)
Common Exceptions:
ValueError- Invalid data conversionZeroDivisionError- Division by zeroFileNotFoundError- Missing fileIndexError- List index out of rangeKeyError- Dictionary key not found
Best Practices:
- Be specific with exception types
- Use finally for cleanup
- Donβt ignore exceptions
- Use context managers when possible
- Provide helpful error messages
Next: Advanced Exception Handling - taking it to the next level! π