S SmartDocs
시리즈: Python python 553 줄 · 업데이트 2026-02-28

06_error_handling.py

Python/01_beginner_level/06_error_handling.py

"""
Error Handling - Making Programs Robust
This file contains all the example code from the Error Handling lesson.
"""

# Understanding Errors and Exceptions

# Common exceptions and when they occur (commented out to avoid actual errors)

# ZeroDivisionError
# result = 10 / 0  # This would raise ZeroDivisionError

# ValueError
# number = int("abc")  # This would raise ValueError

# TypeError
# result = "hello" + 5  # This would raise TypeError

# IndexError
# my_list = [1, 2, 3]
# value = my_list[5]  # This would raise IndexError

# KeyError
# my_dict = {"a": 1, "b": 2}
# value = my_dict["c"]  # This would raise KeyError

# FileNotFoundError
# with open("nonexistent.txt", "r") as file:  # This would raise FileNotFoundError
#     content = file.read()

# AttributeError
# my_string = "hello"
# my_string.append("world")  # This would raise AttributeError

# Basic Exception Handling (try/except)

# Simple try/except Block
def safe_divide(a, b):
    """Safely divide two numbers."""
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        return "Error: Cannot divide by zero"

print("=== Basic Exception Handling ===")
# Test the function
print(safe_divide(10, 2))  # Output: 5.0
print(safe_divide(10, 0))  # Output: Error: Cannot divide by zero

# Handling Multiple Exceptions
def get_user_input():
    """Get and validate user input."""
    try:
        age = int(input("Enter your age: "))
        return age
    except ValueError:
        print("Error: Please enter a valid number")
        return None
    except KeyboardInterrupt:
        print("\nOperation cancelled by user")
        return None

# Test the function (commented out to avoid interactive input)
# age = get_user_input()
# if age is not None:
#     print(f"You are {age} years old")

# Catching Multiple Exception Types
def process_data(data, index):
    """Process data with multiple possible errors."""
    try:
        value = data[index]
        result = value * 2
        return result
    except (IndexError, TypeError):
        print("Error: Invalid index or data type")
        return None
    except ValueError:
        print("Error: Invalid value")
        return None
    except Exception as e:
        print(f"Unexpected error: {e}")
        return None

print("\n=== Multiple Exception Types ===")
# Test the function
numbers = [1, 2, 3, 4, 5]
print(process_data(numbers, 2))  # Output: 6
print(process_data(numbers, 10))  # Output: Error: Invalid index or data type

# Advanced Exception Handling

# try/except/else Block
def read_file_safely(filename):
    """Read a file with proper error handling."""
    try:
        with open(filename, 'r') as file:
            content = file.read()
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found")
        return None
    except PermissionError:
        print(f"Error: Permission denied to read '{filename}'")
        return None
    except Exception as e:
        print(f"Unexpected error: {e}")
        return None
    else:
        print("File read successfully")
        return content

# Test the function
print("\n=== try/except/else ===")
content = read_file_safely("nonexistent.txt")  # This will show error message
if content:
    print(f"Content length: {len(content)}")

# try/except/finally Block
def process_file(filename):
    """Process a file with cleanup in finally block."""
    file = None
    try:
        file = open(filename, 'r')
        content = file.read()
        print(f"File content: {content}")
        return content
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found")
        return None
    except Exception as e:
        print(f"Error processing file: {e}")
        return None
    finally:
        if file:
            file.close()
            print("File closed successfully")

# Test the function
print("\n=== try/except/finally ===")
result = process_file("nonexistent.txt")

# Using with Statement (Context Manager)
def read_file_with_context(filename):
    """Read file using context manager (recommended approach)."""
    try:
        with open(filename, 'r') as file:
            content = file.read()
            return content
    except FileNotFoundError:
        print(f"Error: File '{filename}' not found")
        return None
    except Exception as e:
        print(f"Error reading file: {e}")
        return None

# Test the function
print("\n=== Context Manager ===")
content = read_file_with_context("nonexistent.txt")
if content:
    print(f"Content: {content}")

# Custom Exceptions

class CustomError(Exception):
    """Base class for custom exceptions."""
    pass

class ValidationError(CustomError):
    """Raised when validation fails."""
    def __init__(self, message, field=None):
        self.message = message
        self.field = field
        super().__init__(self.message)

class InsufficientFundsError(CustomError):
    """Raised when account has insufficient funds."""
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        self.message = f"Insufficient funds. Balance: {balance}, Required: {amount}"
        super().__init__(self.message)

print("\n=== Custom Exceptions ===")

# Using custom exceptions
def validate_age(age):
    """Validate age input."""
    if not isinstance(age, int):
        raise ValidationError("Age must be an integer", "age")
    if age < 0:
        raise ValidationError("Age cannot be negative", "age")
    if age > 150:
        raise ValidationError("Age cannot exceed 150", "age")
    return True

def withdraw_money(balance, amount):
    """Withdraw money from account."""
    if amount <= 0:
        raise ValidationError("Withdrawal amount must be positive")
    if balance < amount:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

# Test custom exceptions
try:
    validate_age(-5)
except ValidationError as e:
    print(f"Validation error: {e.message} (Field: {e.field})")

try:
    new_balance = withdraw_money(100, 150)
except InsufficientFundsError as e:
    print(f"Transaction failed: {e.message}")

# Practical Examples

# Example 1: Robust Calculator
def robust_calculator():
    """A calculator with comprehensive error handling."""
    
    def safe_divide(a, b):
        """Safely divide two numbers."""
        try:
            return a / b
        except ZeroDivisionError:
            raise ValueError("Cannot divide by zero")
        except TypeError:
            raise ValueError("Both arguments must be numbers")
    
    def safe_power(base, exponent):
        """Safely calculate power."""
        try:
            result = base ** exponent
            if not isinstance(result, (int, float)):
                raise ValueError("Result is not a valid number")
            return result
        except (OverflowError, ZeroDivisionError):
            raise ValueError("Result is too large or invalid")
        except TypeError:
            raise ValueError("Both arguments must be numbers")
    
    while True:
        print("\n=== Robust Calculator ===")
        print("1. Add")
        print("2. Subtract")
        print("3. Multiply")
        print("4. Divide")
        print("5. Power")
        print("6. Square root")
        print("7. Exit")
        
        choice = input("Enter your choice: ")
        
        if choice == "7":
            print("Goodbye!")
            break
        
        if choice in ["1", "2", "3", "4", "5", "6"]:
            try:
                if choice == "6":  # Square root only needs one number
                    num = float(input("Enter number: "))
                else:
                    num1 = float(input("Enter first number: "))
                    num2 = float(input("Enter second number: "))
                
                if choice == "1":
                    result = num1 + num2
                elif choice == "2":
                    result = num1 - num2
                elif choice == "3":
                    result = num1 * num2
                elif choice == "4":
                    result = safe_divide(num1, num2)
                elif choice == "5":
                    result = safe_power(num1, num2)
                elif choice == "6":
                    if num < 0:
                        raise ValueError("Cannot calculate square root of negative number")
                    result = num ** 0.5
                
                print(f"Result: {result}")
                
            except ValueError as e:
                print(f"Error: {e}")
            except KeyboardInterrupt:
                print("\nOperation cancelled by user")
            except Exception as e:
                print(f"Unexpected error: {e}")
        
        else:
            print("Invalid choice!")

# Example 2: File Processing with Error Handling
def file_processor():
    """Process files with comprehensive error handling."""
    
    def read_file(filename):
        """Read file content safely."""
        try:
            with open(filename, 'r', encoding='utf-8') as file:
                return file.read()
        except FileNotFoundError:
            raise FileNotFoundError(f"File '{filename}' not found")
        except PermissionError:
            raise PermissionError(f"Permission denied to read '{filename}'")
        except UnicodeDecodeError:
            raise UnicodeDecodeError(f"Cannot decode file '{filename}'")
        except Exception as e:
            raise Exception(f"Unexpected error reading '{filename}': {e}")
    
    def write_file(filename, content):
        """Write content to file safely."""
        try:
            with open(filename, 'w', encoding='utf-8') as file:
                file.write(content)
            return True
        except PermissionError:
            raise PermissionError(f"Permission denied to write '{filename}'")
        except Exception as e:
            raise Exception(f"Unexpected error writing '{filename}': {e}")
    
    def process_text_file(filename):
        """Process a text file with various operations."""
        try:
            # Read file
            content = read_file(filename)
            
            # Process content
            lines = content.split('\n')
            word_count = len(content.split())
            char_count = len(content)
            
            # Create report
            report = f"""
                File Analysis Report for: {filename}
                ================================
                Lines: {len(lines)}
                Words: {word_count}
                Characters: {char_count}
                First 100 characters: {content[:100]}...
                """
            
            # Write report
            report_filename = filename.replace('.txt', '_report.txt')
            write_file(report_filename, report)
            
            print(f"File processed successfully!")
            print(f"Report saved as: {report_filename}")
            
        except (FileNotFoundError, PermissionError, UnicodeDecodeError) as e:
            print(f"File error: {e}")
        except Exception as e:
            print(f"Processing error: {e}")
    
    # Interactive file processor
    while True:
        print("\n=== File Processor ===")
        print("1. Process text file")
        print("2. Exit")
        
        choice = input("Enter your choice: ")
        
        if choice == "1":
            filename = input("Enter filename (e.g., example.txt): ")
            process_text_file(filename)
        
        elif choice == "2":
            print("Goodbye!")
            break
        
        else:
            print("Invalid choice!")

# Example 3: Data Validation System
class ValidationError(Exception):
    """Custom exception for validation errors."""
    def __init__(self, message, field=None):
        self.message = message
        self.field = field
        super().__init__(self.message)

def validate_email(email):
    """Validate email format."""
    import re
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+[\.[a-zA-Z]{2}]+$' # Regular expression
                # conie12313@gmail.com.hk
    if not re.match(pattern, email):
        raise ValidationError("Invalid email format", "email")
    return True

def validate_phone(phone):
    """Validate phone number format."""
    import re
    # Remove all non-digit characters
    digits_only = re.sub(r'\D', '', phone) # +1 (234) 567-8901
    if len(digits_only) != 10:
        raise ValidationError("Phone number must have exactly 10 digits", "phone")
    return True

def validate_age(age):
    """Validate age."""
    try:
        age = int(age)
        if age < 0:
            raise ValidationError("Age cannot be negative", "age")
        if age > 150:
            raise ValidationError("Age cannot exceed 150", "age")
        return True
    except ValueError:
        raise ValidationError("Age must be a number", "age")

def user_registration():
    """User registration with comprehensive validation."""
    
    user_data = {}
    
    print("=== User Registration ===")
    
    # Name validation
    while True:
        try:
            name = input("Enter full name: ").strip()
            if not name or len(name) < 2:
                raise ValidationError("Name must be at least 2 characters long", "name")
            if not name.replace(" ", "").isalpha():
                raise ValidationError("Name can only contain letters and spaces", "name")
            user_data['name'] = name
            break
        except ValidationError as e:
            print(f"Error: {e.message}")
    
    # Email validation
    while True:
        try:
            email = input("Enter email: ").strip()
            validate_email(email)
            user_data['email'] = email
            break
        except ValidationError as e:
            print(f"Error: {e.message}")
    
    # Phone validation
    while True:
        try:
            phone = input("Enter phone number: ").strip()
            validate_phone(phone)
            user_data['phone'] = phone
            break
        except ValidationError as e:
            print(f"Error: {e.message}")
    
    # Age validation
    while True:
        try:
            age = input("Enter age: ").strip()
            validate_age(age)
            user_data['age'] = int(age)
            break
        except ValidationError as e:
            print(f"Error: {e.message}")
    
    # Password validation
    while True:
        try:
            password = input("Enter password: ")
            if len(password) < 8:
                raise ValidationError("Password must be at least 8 characters long", "password")
            if not any(c.isupper() for c in password):
                raise ValidationError("Password must contain at least one uppercase letter", "password")
            if not any(c.islower() for c in password):
                raise ValidationError("Password must contain at least one lowercase letter", "password")
            if not any(c.isdigit() for c in password):
                raise ValidationError("Password must contain at least one digit", "password")
            
            confirm_password = input("Confirm password: ")
            if password != confirm_password:
                raise ValidationError("Passwords do not match", "password")
            
            user_data['password'] = password
            break
        except ValidationError as e:
            print(f"Error: {e.message}")
    
    print("\nRegistration successful!")
    print(f"User data: {user_data}")
    
    return user_data

# Best Practices Examples

print("\n=== Best Practices ===")

# 1. Specific Exception Handling
def specific_error_handling():
    """Demonstrate specific exception handling."""
    my_dict = {"a": 1, "b": 2}
    
    # Good: Specific exception handling
    try:
        value = my_dict["c"]
    except KeyError:
        print("Key not found")
    
    # Avoid: Catching all exceptions (commented out)
    # try:
    #     value = my_dict["c"]
    # except Exception:  # Too broad
    #     print("Something went wrong")

specific_error_handling()

# 2. Meaningful Error Messages
def divide_numbers(a, b):
    """Divide two numbers with meaningful error messages."""
    try:
        return a / b
    except ZeroDivisionError:
        raise ValueError(f"Cannot divide {a} by zero")
    except TypeError:
        raise TypeError(f"Both arguments must be numbers, got {type(a)} and {type(b)}")

try:
    result = divide_numbers(10, "2")
except (ValueError, TypeError) as e:
    print(f"Error: {e}")

# 3. Logging Errors
import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def process_data(data):
    """Process data with logging."""
    try:
        result = data * 2
        logging.info(f"Data processed successfully: {result}")
        return result
    except Exception as e:
        logging.error(f"Error processing data: {e}")
        raise

# Test logging
try:
    process_data("hello")
except Exception as e:
    print(f"Caught error: {e}")

# Uncomment to run any of the interactive examples:
# robust_calculator()
# file_processor()
# user_registration()

관련 글