Skip to content

⚠️ Python 异常处理

异常处理概述

异常处理是编程中处理错误和异常情况的重要机制。Python 提供了强大的异常处理系统,让程序能够优雅地处理各种错误情况。

💡 为什么需要异常处理?: - 提高程序健壮性:程序不会因为错误而崩溃 - 提供错误信息:帮助开发者定位和解决问题 - 优雅降级:在错误发生时提供备用方案 - 用户体验:给用户友好的错误提示

🚨 异常类型

常见内置异常

python
# 除零错误
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"除零错误: {e}")

# 类型错误
try:
    result = "hello" + 5
except TypeError as e:
    print(f"类型错误: {e}")

# 值错误
try:
    number = int("abc")
except ValueError as e:
    print(f"值错误: {e}")

# 索引错误
try:
    numbers = [1, 2, 3]
    print(numbers[10])
except IndexError as e:
    print(f"索引错误: {e}")

# 键错误
try:
    person = {"name": "张三"}
    print(person["age"])
except KeyError as e:
    print(f"键错误: {e}")

# 文件不存在错误
try:
    with open("不存在的文件.txt", "r") as f:
        content = f.read()
except FileNotFoundError as e:
    print(f"文件不存在: {e}")

异常层次结构

python
# 查看异常层次结构
import builtins

def print_exception_hierarchy(exception_class, indent=0):
    """打印异常层次结构"""
    print("  " * indent + exception_class.__name__)
    for subclass in exception_class.__subclasses__():
        print_exception_hierarchy(subclass, indent + 1)

# 打印主要异常层次
print("Python 异常层次结构:")
print_exception_hierarchy(BaseException)

🛡️ try-except 语句

基本异常处理

python
def safe_divide(a, b):
    """安全除法函数"""
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("错误:除数不能为零")
        return None
    except TypeError:
        print("错误:参数类型不正确")
        return None

# 测试异常处理
print(safe_divide(10, 2))    # 正常情况
print(safe_divide(10, 0))    # 除零错误
print(safe_divide(10, "2"))  # 类型错误

多个异常处理

python
def process_user_input(user_input):
    """处理用户输入"""
    try:
        # 尝试转换为整数
        number = int(user_input)
        # 尝试计算平方根
        import math
        result = math.sqrt(number)
        return f"√{number} = {result:.2f}"
    
    except ValueError:
        return "错误:请输入有效的数字"
    
    except TypeError:
        return "错误:输入类型不正确"
    
    except Exception as e:
        return f"未知错误:{e}"

# 测试多种情况
test_inputs = ["16", "abc", "-4", "0"]
for inp in test_inputs:
    print(f"输入: {inp} -> {process_user_input(inp)}")

捕获所有异常

python
def risky_operation():
    """可能出错的操作"""
    try:
        # 模拟可能出错的操作
        data = {"name": "张三", "age": 25}
        print(data["name"])
        print(data["age"])
        # 故意制造错误
        print(data["salary"])  # 键不存在
        
    except Exception as e:
        print(f"发生错误: {type(e).__name__}: {e}")
        print("程序继续执行...")

risky_operation()

🔄 try-except-else-finally

完整的异常处理结构

python
def file_operations(filename):
    """文件操作示例"""
    file = None
    try:
        # 尝试打开文件
        file = open(filename, 'r')
        content = file.read()
        print("文件读取成功")
        
    except FileNotFoundError:
        print(f"错误:文件 {filename} 不存在")
        
    except PermissionError:
        print(f"错误:没有权限访问文件 {filename}")
        
    except Exception as e:
        print(f"未知错误:{e}")
        
    else:
        # 没有异常时执行
        print("文件操作完成,没有发生异常")
        return content
        
    finally:
        # 无论是否发生异常都会执行
        if file:
            file.close()
            print("文件已关闭")

# 测试文件操作
file_operations("test.txt")

实际应用示例

python
def safe_json_parse(json_string):
    """安全解析JSON字符串"""
    import json
    
    try:
        data = json.loads(json_string)
        print("JSON解析成功")
        
    except json.JSONDecodeError as e:
        print(f"JSON格式错误:{e}")
        return None
        
    except Exception as e:
        print(f"其他错误:{e}")
        return None
        
    else:
        print("JSON数据有效")
        return data
        
    finally:
        print("JSON解析操作完成")

# 测试JSON解析
valid_json = '{"name": "张三", "age": 25}'
invalid_json = '{"name": "张三", "age": 25'  # 缺少右括号

safe_json_parse(valid_json)
print("-" * 30)
safe_json_parse(invalid_json)

🎯 自定义异常

创建自定义异常

python
class CustomError(Exception):
    """自定义异常基类"""
    pass

class ValidationError(CustomError):
    """验证错误"""
    def __init__(self, message, field=None):
        self.message = message
        self.field = field
        super().__init__(self.message)

class BusinessLogicError(CustomError):
    """业务逻辑错误"""
    def __init__(self, message, error_code=None):
        self.message = message
        self.error_code = error_code
        super().__init__(self.message)

# 使用自定义异常
def validate_age(age):
    """验证年龄"""
    if not isinstance(age, int):
        raise ValidationError("年龄必须是整数", "age")
    if age < 0:
        raise ValidationError("年龄不能为负数", "age")
    if age > 150:
        raise ValidationError("年龄不能超过150岁", "age")
    return True

def process_user_data(name, age):
    """处理用户数据"""
    try:
        validate_age(age)
        if len(name) < 2:
            raise BusinessLogicError("姓名太短", "NAME_TOO_SHORT")
        return f"用户 {name},年龄 {age} 验证通过"
        
    except ValidationError as e:
        return f"验证失败:{e.message} (字段: {e.field})"
        
    except BusinessLogicError as e:
        return f"业务逻辑错误:{e.message} (错误代码: {e.error_code})"

# 测试自定义异常
print(process_user_data("张三", 25))      # 正常
print(process_user_data("张", 25))        # 姓名太短
print(process_user_data("张三", -5))      # 年龄为负
print(process_user_data("张三", "25"))    # 年龄类型错误

异常链

python
class DatabaseError(Exception):
    """数据库错误"""
    pass

class UserService:
    """用户服务类"""
    
    def get_user(self, user_id):
        try:
            # 模拟数据库操作
            if user_id < 0:
                raise DatabaseError("用户ID不能为负数")
            return {"id": user_id, "name": f"用户{user_id}"}
            
        except DatabaseError as e:
            # 重新抛出异常,保留原始异常信息
            raise UserServiceError(f"获取用户失败: {e}") from e

class UserServiceError(Exception):
    """用户服务错误"""
    pass

# 测试异常链
service = UserService()
try:
    user = service.get_user(-1)
except UserServiceError as e:
    print(f"用户服务错误: {e}")
    print(f"原始错误: {e.__cause__}")

🔧 异常处理最佳实践

具体异常处理

python
# 好的做法:捕获具体异常
def good_practice():
    try:
        with open("config.json", "r") as f:
            config = f.read()
    except FileNotFoundError:
        print("配置文件不存在,使用默认配置")
    except PermissionError:
        print("没有权限读取配置文件")
    except Exception as e:
        print(f"读取配置文件时发生未知错误: {e}")

# 不好的做法:捕获所有异常
def bad_practice():
    try:
        with open("config.json", "r") as f:
            config = f.read()
    except:  # 太宽泛
        print("发生错误")

异常处理中的资源管理

python
class DatabaseConnection:
    """数据库连接类"""
    
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.connected = False
    
    def connect(self):
        """连接数据库"""
        try:
            # 模拟连接过程
            if self.port < 0:
                raise ConnectionError("端口号不能为负数")
            self.connected = True
            print(f"已连接到 {self.host}:{self.port}")
        except ConnectionError as e:
            print(f"连接失败: {e}")
            raise
    
    def disconnect(self):
        """断开连接"""
        if self.connected:
            self.connected = False
            print("数据库连接已断开")
    
    def __enter__(self):
        """上下文管理器入口"""
        self.connect()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口"""
        self.disconnect()
        if exc_type:
            print(f"操作过程中发生异常: {exc_type.__name__}")
        return False  # 不抑制异常

# 使用上下文管理器
try:
    with DatabaseConnection("localhost", 3306) as db:
        print("执行数据库操作...")
        # 模拟操作
        if db.connected:
            print("操作成功")
except ConnectionError as e:
    print(f"数据库操作失败: {e}")

日志记录

python
import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def process_data(data):
    """处理数据,记录异常"""
    try:
        # 模拟数据处理
        if not data:
            raise ValueError("数据不能为空")
        
        result = [x * 2 for x in data]
        logging.info(f"数据处理成功,处理了 {len(data)} 条记录")
        return result
        
    except ValueError as e:
        logging.error(f"数据验证失败: {e}")
        raise
        
    except Exception as e:
        logging.error(f"数据处理时发生未知错误: {e}")
        raise

# 测试日志记录
try:
    result = process_data([1, 2, 3, 4, 5])
    print(f"处理结果: {result}")
except Exception as e:
    print(f"处理失败: {e}")

🎯 实践练习

练习1:计算器异常处理

编写一个安全的计算器,处理以下异常:

  • 除零错误
  • 无效输入
  • 数学运算错误

练习2:文件处理异常

编写一个文件处理程序,处理:

  • 文件不存在
  • 权限不足
  • 文件格式错误
  • 磁盘空间不足

练习3:网络请求异常

模拟网络请求,处理:

  • 连接超时
  • 网络不可达
  • 服务器错误
  • 数据格式错误

练习4:用户输入验证

创建一个用户注册系统,验证:

  • 用户名格式
  • 密码强度
  • 邮箱格式
  • 年龄范围

🔍 调试技巧

异常信息分析

python
import traceback

def analyze_exception():
    """分析异常信息"""
    try:
        # 故意制造一个复杂的异常
        data = {"users": [{"name": "张三", "age": 25}]}
        user = data["users"][1]  # 索引超出范围
        print(user["salary"])    # 键不存在
        
    except Exception as e:
        print(f"异常类型: {type(e).__name__}")
        print(f"异常信息: {e}")
        print(f"异常参数: {e.args}")
        print("\n完整异常信息:")
        traceback.print_exc()

analyze_exception()

异常处理装饰器

python
def handle_exceptions(func):
    """异常处理装饰器"""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print(f"函数 {func.__name__} 发生异常: {e}")
            return None
    return wrapper

@handle_exceptions
def risky_function(x, y):
    """可能出错的函数"""
    return x / y

# 使用装饰器
result = risky_function(10, 0)
print(f"结果: {result}")

📊 异常处理总结

异常处理原则

  1. 具体优于宽泛:捕获具体异常而不是所有异常
  2. 早发现早处理:在问题发生的地方处理异常
  3. 提供有用信息:异常信息应该帮助定位问题
  4. 优雅降级:提供备用方案或默认值
  5. 记录日志:记录异常信息用于调试
  6. 资源清理:使用 finally 或上下文管理器清理资源

常见异常类型

异常类型描述常见场景
ValueError值错误类型转换失败
TypeError类型错误操作不支持的类型
IndexError索引错误列表索引超出范围
KeyError键错误字典键不存在
FileNotFoundError文件不存在打开不存在的文件
ZeroDivisionError除零错误除数为零

下一步

现在你已经掌握了 Python 的异常处理,接下来学习:


💡 学习建议:多练习异常处理,提高程序的健壮性和用户体验