Python 第十七节 开发使用中异常及错误详解
Python中的错误处理主要包括语法错误和运行时异常。语法错误如缺少冒号、缩进错误等会在运行前被检测到;常见运行时异常包括ZeroDivisionError、TypeError等。通过try-except机制可以捕获处理异常,结合else和finally子句完善处理流程。raise可主动抛出异常,还能自定义异常类。实际应用中,文件操作和API调用需特别注意异常处理,如检查文件存在性、处理网络请求超
·
Python 使用中学习各种语法错误与异常处理,有助于我们快速定位问题
1. 语法错误 (Syntax Errors)
语法错误是代码编写时不符合 Python 语法规则的错误,在程序运行前就会被解释器检测到。
1.1、常见语法错误类型
# 1. 缺少冒号
if True # 错误:缺少冒号
print("Hello")
# 2. 缩进错误
def func():
print("Hello") # 错误:函数体内缺少缩进
# 3. 括号不匹配
print("Hello" # 错误:缺少右括号
# 4. 使用关键字作为变量名
class = "Math" # 错误:class 是关键字
# 5. 无效的语法结构
for i in range(5)
print(i) # 错误:for 循环缺少冒号
2. 异常 (Exceptions)
异常是程序运行时发生的错误,即使语法正确也可能发生。
常见异常类型
# 1. ZeroDivisionError: 除零错误
result = 10 / 0
# 2. TypeError: 类型错误
result = "hello" + 5
# 3. ValueError: 值错误
number = int("abc")
# 4. IndexError: 索引错误
my_list = [1, 2, 3]
item = my_list[5]
# 5. KeyError: 键错误
my_dict = {"a": 1, "b": 2}
value = my_dict["c"]
# 6. FileNotFoundError: 文件未找到
with open("nonexistent.txt", "r") as f:
content = f.read()
# 7. AttributeError: 属性错误
x = 10
x.append(5)
3. 异常处理机制
3.1 try-except 基础
try:
# 可能引发异常的代码
number = int(input("请输入一个数字: "))
result = 100 / number
print(f"结果是: {result}")
except ValueError:
print("错误:请输入有效的数字!")
except ZeroDivisionError:
print("错误:不能除以零!")
3.2 捕获多个异常
try:
# 可能引发异常的代码
file = open("data.txt", "r")
data = file.read()
number = int(data.strip())
result = 100 / number
except (FileNotFoundError, ValueError, ZeroDivisionError) as e:
print(f"发生错误: {e}")
print(f"错误类型: {type(e).__name__}")
3.3 else 和 finally 子句
def read_and_process_file(filename):
try:
file = open(filename, "r")
data = file.read()
number = int(data.strip())
except FileNotFoundError:
print("文件不存在")
return None
except ValueError:
print("文件内容不是有效数字")
return None
else:
# 如果没有异常发生,执行这里的代码
print("文件读取成功")
return number
finally:
# 无论是否发生异常,都会执行
if 'file' in locals() and not file.closed:
file.close()
print("文件已关闭")
# 测试
result = read_and_process_file("data.txt")
3.4 捕获所有异常
try:
# 可能引发异常的代码
risky_operation()
except Exception as e:
print(f"发生未知错误: {e}")
# 记录日志或进行其他处理
4. 抛出异常 (Raising Exceptions)
4.1 使用 raise 抛出异常
def validate_age(age):
if age < 0:
raise ValueError("年龄不能为负数")
if age > 150:
raise ValueError("年龄不能超过150岁")
return True
try:
validate_age(-5)
except ValueError as e:
print(f"验证失败: {e}")
4.2 重新抛出异常
def process_data(data):
try:
# 尝试处理数据
result = int(data)
except ValueError:
print("数据格式错误,记录日志后重新抛出异常")
# 记录日志后重新抛出原始异常
raise
try:
process_data("abc")
except ValueError as e:
print(f"外层捕获到异常: {e}")
5. 自定义异常
class InsufficientFundsError(Exception):
"""自定义异常:余额不足"""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
self.message = f"余额不足: 当前余额 {balance},需要 {amount}"
super().__init__(self.message)
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
return self.balance
# 使用自定义异常
account = BankAccount(100)
try:
account.withdraw(200)
except InsufficientFundsError as e:
print(f"取款失败: {e}")
6. 实际案例分析
6.1、文件处理异常处理
import os
def safe_file_operations(filename):
"""
安全的文件操作函数,包含完整的异常处理
"""
file = None
try:
# 检查文件是否存在
if not os.path.exists(filename):
raise FileNotFoundError(f"文件 {filename} 不存在")
# 尝试打开文件
file = open(filename, 'r', encoding='utf-8')
# 读取文件内容
content = file.read()
# 处理内容
if not content:
raise ValueError("文件内容为空")
processed_content = content.upper()
# 写入新文件
output_filename = f"processed_{filename}"
with open(output_filename, 'w', encoding='utf-8') as output_file:
output_file.write(processed_content)
print(f"文件处理完成,输出文件: {output_filename}")
return processed_content
except FileNotFoundError as e:
print(f"文件错误: {e}")
return None
except PermissionError as e:
print(f"权限错误: {e}")
return None
except UnicodeDecodeError 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
finally:
# 确保文件被关闭
if file and not file.closed:
file.close()
print("文件已安全关闭")
# 测试
safe_file_operations("test.txt")
6.2、 API 调用异常处理
import requests
import time
class APIClient:
def __init__(self, base_url, max_retries=3):
self.base_url = base_url
self.max_retries = max_retries
self.session = requests.Session()
def make_request(self, endpoint, params=None, timeout=10):
"""
安全的 API 请求方法,包含重试机制
"""
url = f"{self.base_url}/{endpoint}"
for attempt in range(self.max_retries):
try:
response = self.session.get(url, params=params, timeout=timeout)
response.raise_for_status() # 如果状态码不是200,抛出HTTPError
return response.json()
except requests.exceptions.Timeout:
print(f"请求超时,第 {attempt + 1} 次重试...")
if attempt == self.max_retries - 1:
raise Exception("API 请求超时,已达到最大重试次数")
except requests.exceptions.ConnectionError:
print(f"连接错误,第 {attempt + 1} 次重试...")
if attempt == self.max_retries - 1:
raise Exception("无法连接到服务器")
except requests.exceptions.HTTPError as e:
if response.status_code == 404:
raise Exception("请求的资源不存在")
elif response.status_code == 500:
print(f"服务器错误,第 {attempt + 1} 次重试...")
if attempt == self.max_retries - 1:
raise Exception("服务器内部错误")
else:
raise Exception(f"HTTP 错误: {e}")
except requests.exceptions.RequestException as e:
raise Exception(f"请求异常: {e}")
# 等待后重试
time.sleep(2 ** attempt) # 指数退避
raise Exception("未知错误")
# 使用示例
try:
client = APIClient("https://api.example.com")
data = client.make_request("users", {"id": 123})
print("API 调用成功:", data)
except Exception as e:
print(f"API 调用失败: {e}")
6.3、数据库操作异常处理
import sqlite3
import logging
class DatabaseManager:
def __init__(self, db_path):
self.db_path = db_path
self.setup_logging()
def setup_logging(self):
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
self.logger = logging.getLogger(__name__)
def execute_query(self, query, params=None, fetch=False):
"""
执行数据库查询,包含完整的异常处理
"""
connection = None
try:
# 连接数据库
connection = sqlite3.connect(self.db_path)
cursor = connection.cursor()
# 执行查询
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
# 如果是写操作,提交事务
if query.strip().upper().startswith(('INSERT', 'UPDATE', 'DELETE')):
connection.commit()
self.logger.info("数据库操作已提交")
# 如果需要获取结果
if fetch:
result = cursor.fetchall()
return result
else:
return cursor.rowcount
except sqlite3.OperationalError as e:
self.logger.error(f"数据库操作错误: {e}")
if connection:
connection.rollback()
raise Exception(f"数据库操作失败: {e}")
except sqlite3.IntegrityError as e:
self.logger.error(f"数据完整性错误: {e}")
if connection:
connection.rollback()
raise Exception(f"数据违反完整性约束: {e}")
except sqlite3.DatabaseError as e:
self.logger.error(f"数据库错误: {e}")
if connection:
connection.rollback()
raise Exception(f"数据库错误: {e}")
except Exception as e:
self.logger.error(f"未知错误: {e}")
if connection:
connection.rollback()
raise Exception(f"操作失败: {e}")
finally:
# 确保连接被关闭
if connection:
connection.close()
self.logger.info("数据库连接已关闭")
# 使用示例
def manage_user_database():
db = DatabaseManager("test.db")
# 创建表
create_table_sql = """
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER CHECK(age >= 0)
)
"""
try:
db.execute_query(create_table_sql)
print("表创建成功")
# 插入数据
insert_sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"
db.execute_query(insert_sql, ("Alice", "alice@example.com", 25))
print("数据插入成功")
# 查询数据
select_sql = "SELECT * FROM users WHERE age > ?"
users = db.execute_query(select_sql, (20,), fetch=True)
print("查询结果:", users)
except Exception as e:
print(f"数据库操作失败: {e}")
# 运行示例
manage_user_database()
7. 最佳实践总结
- 具体异常优先: 捕获具体的异常类型,而不是通用的
Exception - 避免空
except: 不要使用空的except块,至少要记录日志 - 资源清理: 使用
finally或上下文管理器确保资源被正确释放 - 异常信息明确: 提供清晰的错误信息,便于调试
- 适当重试: 对于临时性错误,实现适当的重试机制
- 日志记录: 记录异常信息,便于问题追踪
- 自定义异常: 为业务逻辑错误创建自定义异常类型
通过合理的异常处理,可以提高我们的程序的健壮性和可维护性。
更多推荐
所有评论(0)