# 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}")
# 相关资源
- python-office 官方文档 - Python自动化办公库
- AI + 自动化办公课程 - 35讲AI办公自动化实战
- Python 实用技巧 - 更多Python技巧
- 📺 视频课程:Python入门15讲
- 👥 技术交流:加入读者群
# 总结
良好的异常处理是编写健壮程序的基础。掌握try-except结构、自定义异常、日志记录、重试机制等技巧,让你的代码能够优雅地处理各种错误情况,提升程序的可靠性!