Python错误处理艺术:从崩溃到优雅恢复的蜕变

错误处理让你的代码在遇到问题时不会立即崩溃,而是优雅地处理异常情况,继续运行或给出有意义的提示

为什么错误处理如此重要?

看看这个没有错误处理的危险代码:

python 复制代码
# 危险!没有错误处理的代码
def calculate_discount(price, discount):
    return price * (1 - discount)

# 正常情况没问题
print(calculate_discount(100, 0.2))  # 输出: 80.0

# 但如果有异常输入...
print(calculate_discount(100, "20%"))  # 程序崩溃!
print(calculate_discount("100元", 0.2))  # 程序崩溃!

一次意外的输入,整个程序就崩溃了

Python错误处理基础:try-except的魔力

基本用法

python 复制代码
def safe_calculate_discount(price, discount):
    try:
        # 尝试执行可能出错的代码
        result = price * (1 - discount)
        return result
    except TypeError:
        # 如果发生类型错误,执行这里
        print("错误:价格和折扣必须是数字!")
        return None

# 现在程序不会崩溃了
print(safe_calculate_discount(100, 0.2))      # 正常: 80.0
print(safe_calculate_discount(100, "20%"))    # 优雅处理: 错误:价格和折扣必须是数字! None

处理多种异常

python 复制代码
def process_user_data(user_data):
    try:
        name = user_data["name"]
        age = user_data["age"]
        email = user_data["email"]
        
        # 模拟一些业务逻辑
        if age < 0:
            raise ValueError("年龄不能为负数")
            
        return f"用户 {name} ({age}岁) 邮箱: {email}"
        
    except KeyError as e:
        print(f"数据不完整:缺少字段 {e}")
        return None
    except ValueError as e:
        print(f"数据验证失败:{e}")
        return None
    except Exception as e:
        print(f"未知错误:{e}")
        return None

# 测试各种异常情况
test_cases = [
    {"name": "张三", "age": 25, "email": "zhang@example.com"},  # 正常
    {"name": "李四", "age": -5, "email": "li@example.com"},     # 年龄为负
    {"name": "王五", "age": 30},                                # 缺少邮箱
    "invalid data"                                              # 完全错误的数据
]

for data in test_cases:
    result = process_user_data(data)
    print(f"处理结果: {result}")

高级错误处理技巧

else和finally子句

pythondef 复制代码
    """读取配置文件,演示完整的错误处理结构"""
    config = {}
    file = None
    
    try:
        print(f"尝试打开文件: {filename}")
        file = open(filename, 'r', encoding='utf-8')
        content = file.read()
        config = eval(content)  # 这里可能出错
        
    except FileNotFoundError:
        print(f"文件 {filename} 不存在")
        
    except SyntaxError:
        print("配置文件语法错误")
        
    except Exception as e:
        print(f"读取文件时发生错误: {e}")
        
    else:
        # 只有在try块成功执行时才运行
        print("配置文件读取成功!")
        
    finally:
        # 无论是否发生异常都会执行
        if file:
            file.close()
            print("文件已关闭")
        
    return config

# 测试
config = read_config_file("config.txt")
print(f"配置内容: {config}")

自定义异常类

python 复制代码
class BusinessError(Exception):
    """业务逻辑异常基类"""
    pass

class InsufficientFundsError(BusinessError):
    """余额不足异常"""
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(f"余额不足!当前余额: {balance}, 需要: {amount}")

class InvalidAmountError(BusinessError):
    """无效金额异常"""
    def __init__(self, amount):
        self.amount = amount
        super().__init__(f"无效金额: {amount},必须大于0")

class BankAccount:
    def __init__(self, account_holder, initial_balance=0):
        self.account_holder = account_holder
        self.balance = initial_balance
    
    def withdraw(self, amount):
        """取款操作"""
        if amount <= 0:
            raise InvalidAmountError(amount)
        
        if amount > self.balance:
            raise InsufficientFundsError(self.balance, amount)
        
        self.balance -= amount
        print(f"成功取款 {amount},当前余额: {self.balance}")
        return amount

# 使用自定义异常
account = BankAccount("张三", 1000)

test_amounts = [500, 1500, -100]

for amount in test_amounts:
    try:
        account.withdraw(amount)
    except BusinessError as e:
        print(f"业务操作失败: {e}")
    except Exception as e:
        print(f"系统错误: {e}")

实际应用场景

场景1:API调用错误处理

python 复制代码
import requests
import time

def robust_api_call(url, max_retries=3, timeout=5):
    """健壮的API调用,包含重试机制"""
    
    for attempt in range(max_retries):
        try:
            print(f"第 {attempt + 1} 次尝试调用 API...")
            response = requests.get(url, timeout=timeout)
            response.raise_for_status()  # 如果状态码不是200,抛出异常
            
            # 成功获取响应
            data = response.json()
            print("API调用成功!")
            return data
            
        except requests.exceptions.Timeout:
            print(f"请求超时 (尝试 {attempt + 1}/{max_retries})")
            
        except requests.exceptions.ConnectionError:
            print(f"连接错误 (尝试 {attempt + 1}/{max_retries})")
            
        except requests.exceptions.HTTPError as e:
            print(f"HTTP错误: {e.response.status_code}")
            # 4xx错误不需要重试
            if 400 <= e.response.status_code < 500:
                break
                
        except Exception as e:
            print(f"未知错误: {e}")
        
        # 等待后重试
        if attempt < max_retries - 1:
            wait_time = 2 ** attempt  # 指数退避
            print(f"等待 {wait_time} 秒后重试...")
            time.sleep(wait_time)
    
    print("所有重试尝试都失败了")
    return None

# 测试
# result = robust_api_call("https://api.example.com/data")

场景2:数据库操作错误处理

python 复制代码
import sqlite3
from contextlib import contextmanager

@contextmanager
def database_connection(db_path):
    """数据库连接上下文管理器,自动处理连接和事务"""
    conn = None
    try:
        conn = sqlite3.connect(db_path)
        conn.row_factory = sqlite3.Row  # 返回字典样式的行
        print("数据库连接成功")
        yield conn
        conn.commit()
        print("事务提交成功")
        
    except sqlite3.Error as e:
        if conn:
            conn.rollback()
            print(f"数据库错误,已回滚: {e}")
        raise
        
    finally:
        if conn:
            conn.close()
            print("数据库连接已关闭")

def safe_database_operation():
    """安全的数据库操作示例"""
    try:
        with database_connection(":memory:") as conn:
            # 创建表
            conn.execute('''
                CREATE TABLE users (
                    id INTEGER PRIMARY KEY,
                    name TEXT NOT NULL,
                    email TEXT UNIQUE NOT NULL
                )
            ''')
            
            # 插入数据
            users = [
                (1, "张三", "zhang@example.com"),
                (2, "李四", "li@example.com"),
                (3, "王五", "wang@example.com")
            ]
            
            for user in users:
                try:
                    conn.execute(
                        "INSERT INTO users (id, name, email) VALUES (?, ?, ?)",
                        user
                    )
                    print(f"成功插入用户: {user[1]}")
                    
                except sqlite3.IntegrityError:
                    print(f"用户 {user[1]} 已存在,跳过插入")
            
            # 查询数据
            cursor = conn.execute("SELECT * FROM users")
            for row in cursor:
                print(f"ID: {row['id']}, 姓名: {row['name']}, 邮箱: {row['email']}")
                
    except Exception as e:
        print(f"数据库操作失败: {e}")

# 运行示例
safe_database_operation()

场景3:文件处理错误处理

python 复制代码
import os
import json

def safe_file_operations():
    """安全的文件操作示例"""
    
    def read_json_file(filename):
        """安全读取JSON文件"""
        try:
            with open(filename, 'r', encoding='utf-8') as file:
                data = json.load(file)
                print(f"成功读取文件: {filename}")
                return data
                
        except FileNotFoundError:
            print(f"文件 {filename} 不存在")
            return {}
            
        except json.JSONDecodeError as e:
            print(f"JSON解析错误: {e}")
            return {}
            
        except PermissionError:
            print(f"没有权限读取文件: {filename}")
            return {}
    
    def write_json_file(filename, data):
        """安全写入JSON文件"""
        try:
            # 确保目录存在
            os.makedirs(os.path.dirname(filename) or '.', exist_ok=True)
            
            with open(filename, 'w', encoding='utf-8') as file:
                json.dump(data, file, indent=2, ensure_ascii=False)
                print(f"成功写入文件: {filename}")
                return True
                
        except PermissionError:
            print(f"没有权限写入文件: {filename}")
            return False
            
        except Exception as e:
            print(f"写入文件时出错: {e}")
            return False
    
    # 测试文件操作
    test_data = {
        "users": [
            {"name": "张三", "age": 25},
            {"name": "李四", "age": 30}
        ],
        "settings": {
            "theme": "dark",
            "language": "zh-CN"
        }
    }
    
    # 写入文件
    if write_json_file("data/config.json", test_data):
        # 读取文件
        loaded_data = read_json_file("data/config.json")
        print(f"读取的数据: {loaded_data}")
    
    # 测试错误情况
    read_json_file("nonexistent_file.json")  # 文件不存在
    read_json_file("/root/protected_file.json")  # 权限错误

# 运行示例
safe_file_operations()

错误处理的最佳实践

1. 具体化异常捕获

python 复制代码
# 不好 - 太宽泛
try:
    # 一些操作
except:
    pass

# 好 - 具体异常
try:
    # 一些操作
except (ValueError, TypeError) as e:
    print(f"输入数据错误: {e}")

2. 不要静默吞噬异常

python 复制代码
# 不好 - 静默吞噬异常
try:
    risky_operation()
except:
    pass  # 没人知道这里出错了

# 好 - 记录或处理异常
try:
    risky_operation()
except Exception as e:
    logger.error(f"操作失败: {e}")
    # 或者给用户反馈

3. 使用异常链

python 复制代码
def process_data(data):
    try:
        return complex_operation(data)
    except Exception as e:
        # 保留原始异常信息
        raise ProcessingError("数据处理失败") from e
相关推荐
我叫黑大帅1 小时前
什么叫可迭代对象?为什么要用它?
前端·后端·python
Dillon Dong1 小时前
Django + uWSGI 部署至 Ubuntu 完整指南
python·ubuntu·django
k***82512 小时前
python爬虫——爬取全年天气数据并做可视化分析
开发语言·爬虫·python
new_dev2 小时前
Python网络爬虫从入门到实战
爬虫·python·媒体
q***01652 小时前
Python爬虫完整代码拿走不谢
开发语言·爬虫·python
今天没有盐2 小时前
Python算法实战:从滑动窗口到数学可视化
python·pycharm·编程语言
Learn Beyond Limits2 小时前
Data Preprocessing|数据预处理
大数据·人工智能·python·ai·数据挖掘·数据处理
lucky_dog2 小时前
python——课堂笔记😻
python
西部秋虫4 小时前
YOLO 训练车牌定位模型 + OpenCV C++ 部署完整步骤
c++·python·yolo·车牌识别