# Python异常处理:让你的代码更加健壮

编写程序时,错误是不可避免的。良好的异常处理机制可以让你的程序在遇到错误时优雅地处理,而不是直接崩溃。本文详细介绍Python异常处理的各种技巧,让你的代码更加健壮。

# 一、基础异常处理

# try-except结构

# 基础异常处理
try:
    result = 10 / 0
except ZeroDivisionError:
    print("错误:除数不能为零")

# 捕获具体异常
try:
    num = int("abc")
except ValueError:
    print("错误:无法将字符串转换为整数")

# 捕获多个异常
try:
    data = [1, 2, 3]
    print(data[10])
except IndexError:
    print("错误:索引超出范围")
except Exception as e:
    print(f"发生错误: {e}")

# 完整结构

try:
    # 可能发生异常的代码
    result = 10 / 2
except ZeroDivisionError:
    # 处理除零错误
    print("除数不能为零")
except ValueError:
    # 处理值错误
    print("值错误")
except Exception as e:
    # 处理其他异常
    print(f"发生错误: {e}")
else:
    # 如果没有异常,执行这里
    print(f"计算结果: {result}")
finally:
    # 无论是否有异常,都会执行
    print("清理资源")

# 二、常见异常类型

# Python内置异常类型
exceptions = {
    'ZeroDivisionError': '除数为零',
    'ValueError': '值错误',
    'TypeError': '类型错误',
    'IndexError': '索引超出范围',
    'KeyError': '字典键不存在',
    'FileNotFoundError': '文件不存在',
    'PermissionError': '权限不足',
    'AttributeError': '对象没有该属性',
    'NameError': '变量未定义',
    'SyntaxError': '语法错误',
    'ImportError': '导入模块失败',
    'TimeoutError': '操作超时',
    'MemoryError': '内存不足',
    'AssertionError': '断言失败',
    'RuntimeError': '运行时错误'
}

for exc, desc in exceptions.items():
    print(f"{exc}: {desc}")

# 三、自定义异常

# 创建自定义异常

class ValidationError(Exception):
    """数据验证错误"""
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"验证失败 [{field}]: {message}")

class BusinessException(Exception):
    """业务逻辑异常"""
    def __init__(self, code, message):
        self.code = code
        self.message = message
        super().__init__(f"[{code}] {message}")

# 使用自定义异常
def validate_age(age):
    if age < 0:
        raise ValidationError('age', '年龄不能为负数')
    if age > 150:
        raise ValidationError('age', '年龄超出合理范围')
    return True

try:
    validate_age(-5)
except ValidationError as e:
    print(f"验证错误: {e.message}")
    print(f"错误字段: {e.field}")

# 异常链

# 异常链 - 保留原始异常信息
try:
    # 某个操作
    data = [1, 2, 3]
    print(data[10])
except IndexError as e:
    # 转换为业务异常
    raise BusinessException('DATA_ERROR', '获取数据失败') from e

# 使用 raise from 保留异常链
try:
    num = int("abc")
except ValueError as e:
    raise RuntimeError("数字转换失败") from e

# 四、异常处理最佳实践

# 文件操作异常处理

# 不好的写法
def read_file_bad(path):
    f = open(path, 'r')  # 如果文件不存在会崩溃
    content = f.read()
    f.close()  # 如果读取失败,永远不会关闭
    return content

# 好的写法 - 使用 with 自动关闭
def read_file_good(path):
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return f.read()
    except FileNotFoundError:
        print(f"文件不存在: {path}")
        return None
    except PermissionError:
        print(f"权限不足: {path}")
        return None
    except Exception as e:
        print(f"读取文件时发生错误: {e}")
        return None

# 更好的写法 - 返回错误信息
from dataclasses import dataclass
from typing import Optional

@dataclass
class Result:
    success: bool
    data: Optional[str] = None
    error: Optional[str] = None

def read_file_best(path) -> Result:
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return Result(success=True, data=f.read())
    except FileNotFoundError:
        return Result(success=False, error=f"文件不存在: {path}")
    except PermissionError:
        return Result(success=False, error=f"权限不足: {path}")
    except Exception as e:
        return Result(success=False, error=str(e))

# 网络请求异常处理

import requests
from requests.exceptions import RequestException, Timeout, ConnectionError

def fetch_data(url, timeout=5):
    """带异常处理的网络请求"""
    try:
        response = requests.get(url, timeout=timeout)
        response.raise_for_status()  # 检查HTTP错误
        return response.json()
    
    except Timeout:
        print(f"请求超时: {url}")
        return None
    except ConnectionError:
        print(f"连接失败: {url}")
        return None
    except requests.exceptions.HTTPError as e:
        print(f"HTTP错误: {e.response.status_code}")
        return None
    except RequestException as e:
        print(f"请求异常: {e}")
        return None

# 使用示例
data = fetch_data('https://api.example.com/data')
if data:
    print(f"获取数据成功: {data}")

# 数据验证异常处理

from dataclasses import dataclass
from typing import List, Optional

@dataclass
class ValidationResult:
    is_valid: bool
    errors: List[str]

def validate_user_data(name: str, age: int, email: str) -> ValidationResult:
    errors = []
    
    # 验证姓名
    if not name:
        errors.append("姓名不能为空")
    elif len(name) < 2:
        errors.append("姓名至少2个字符")
    elif len(name) > 50:
        errors.append("姓名最多50个字符")
    
    # 验证年龄
    if not isinstance(age, int):
        errors.append("年龄必须是整数")
    elif age < 0:
        errors.append("年龄不能为负数")
    elif age > 150:
        errors.append("年龄超出合理范围")
    
    # 验证邮箱
    if not email:
        errors.append("邮箱不能为空")
    elif '@' not in email:
        errors.append("邮箱格式不正确")
    
    return ValidationResult(
        is_valid=len(errors) == 0,
        errors=errors
    )

# 使用
result = validate_user_data("张", 25, "zhang@example.com")
if not result.is_valid:
    print("验证失败:")
    for error in result.errors:
        print(f"  - {error}")

# 五、异常日志记录

# 日志配置

import logging
from datetime import datetime

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(f'error_{datetime.now().strftime("%Y%m%d")}.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

def divide_numbers(a, b):
    try:
        result = a / b
        logger.info(f"{a} / {b} = {result}")
        return result
    except ZeroDivisionError:
        logger.error(f"除数不能为零: {a} / {b}")
        return None
    except Exception as e:
        logger.exception(f"计算错误: {e}")
        return None

# 使用
divide_numbers(10, 2)
divide_numbers(10, 0)

# 异常上下文

import traceback
import sys

def handle_exception(e, context=""):
    """格式化异常信息"""
    exc_type = type(e).__name__
    exc_msg = str(e)
    exc_trace = traceback.format_exc()
    
    error_info = f"""
{'='*50}
异常上下文: {context}
异常类型: {exc_type}
异常信息: {exc_msg}
调用堆栈:
{exc_trace}
{'='*50}
"""
    print(error_info)
    
    # 记录到日志
    with open('exception.log', 'a', encoding='utf-8') as f:
        f.write(error_info)

# 使用
try:
    data = [1, 2, 3]
    print(data[10])
except Exception as e:
    handle_exception(e, context="获取列表元素")

# 六、retry重试机制

import time
from functools import wraps

def retry(max_attempts=3, delay=1, exceptions=(Exception,)):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    attempts += 1
                    if attempts >= max_attempts:
                        raise
                    print(f"尝试 {attempts} 失败,重试中... ({e})")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

# 使用示例
@retry(max_attempts=3, delay=2, exceptions=(ConnectionError, TimeoutError))
def fetch_data_from_api():
    # 模拟网络请求
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络连接失败")
    return "数据获取成功"

# 测试
try:
    result = fetch_data_from_api()
    print(result)
except Exception as e:
    print(f"最终失败: {e}")

# 相关资源

# 总结

良好的异常处理是编写健壮程序的基础。掌握try-except结构、自定义异常、日志记录、重试机制等技巧,让你的代码能够优雅地处理各种错误情况,提升程序的可靠性!

Last Updated: 4/23/2026, 9:22:28 AM