Appearance
Python 错误和异常
在 Python 编程中,错误和异常是不可避免的。本章节将详细介绍 Python 中的错误和异常类型,以及如何有效地处理它们。
错误与异常的区别
- 错误(Error):通常是指程序中的语法错误或逻辑错误,导致程序无法正常执行。
- 异常(Exception):是指程序运行过程中发生的事件,如除以零、文件不存在等,会中断程序的正常执行。
异常类型
Python 内置了许多异常类型,以下是一些常见的异常类型:
| 异常类型 | 描述 |
|---|---|
SyntaxError | 语法错误 |
IndentationError | 缩进错误 |
NameError | 变量名不存在 |
TypeError | 类型错误 |
ValueError | 值错误 |
ZeroDivisionError | 除以零错误 |
FileNotFoundError | 文件不存在错误 |
IOError | I/O 错误 |
ImportError | 导入错误 |
KeyError | 字典键不存在错误 |
IndexError | 索引错误 |
AttributeError | 属性错误 |
OverflowError | 溢出错误 |
RecursionError | 递归错误 |
PermissionError | 权限错误 |
异常处理
Python 使用 try-except 语句来处理异常。
基本语法
python
try:
# 可能会引发异常的代码
pass
except [异常类型 [as 变量名]]:
# 异常处理代码
pass
[else:
# 没有异常时执行的代码
pass]
[finally:
# 无论是否有异常都会执行的代码
pass]示例 1:基本异常处理
python
# 基本异常处理
try:
x = 10 / 0
except ZeroDivisionError:
print("错误:除以零")
print("程序继续执行")输出:
错误:除以零
程序继续执行示例 2:捕获多个异常
python
# 捕获多个异常
try:
x = int(input("请输入一个数字:"))
y = 10 / x
print(f"10 / {x} = {y}")
except ZeroDivisionError:
print("错误:除以零")
except ValueError:
print("错误:请输入有效的数字")
print("程序继续执行")输出:
请输入一个数字:0
错误:除以零
程序继续执行或
请输入一个数字:abc
错误:请输入有效的数字
程序继续执行示例 3:捕获所有异常
python
# 捕获所有异常
try:
x = 10 / 0
except Exception as e:
print(f"发生错误:{e}")
print("程序继续执行")输出:
发生错误:division by zero
程序继续执行示例 4:else 子句
python
# else 子句
try:
x = int(input("请输入一个数字:"))
y = 10 / x
except ZeroDivisionError:
print("错误:除以零")
except ValueError:
print("错误:请输入有效的数字")
else:
print(f"计算成功:10 / {x} = {y}")
print("程序继续执行")输出:
请输入一个数字:2
计算成功:10 / 2 = 5.0
程序继续执行示例 5:finally 子句
python
# finally 子句
try:
f = open("example.txt", "r")
content = f.read()
print(content)
except FileNotFoundError:
print("错误:文件不存在")
finally:
if 'f' in locals() and not f.closed:
f.close()
print("文件已关闭")
print("程序继续执行")输出:
如果文件不存在:
错误:文件不存在
程序继续执行如果文件存在:
文件内容
文件已关闭
程序继续执行抛出异常
使用 raise 语句可以主动抛出异常。
基本语法
python
raise [异常类型 [, 参数]]示例 1:抛出内置异常
python
# 抛出内置异常
def divide(a, b):
if b == 0:
raise ZeroDivisionError("除数不能为零")
return a / b
try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(f"错误:{e}")输出:
错误:除数不能为零示例 2:抛出自定义异常
python
# 抛出自定义异常
class CustomError(Exception):
"""自定义异常类"""
pass
def check_age(age):
if age < 0:
raise CustomError("年龄不能为负数")
elif age > 120:
raise CustomError("年龄不能超过 120")
return age
try:
age = check_age(-5)
except CustomError as e:
print(f"错误:{e}")
try:
age = check_age(150)
except CustomError as e:
print(f"错误:{e}")输出:
错误:年龄不能为负数
错误:年龄不能超过 120自定义异常类
可以通过继承 Exception 类来创建自定义异常类。
示例
python
# 自定义异常类
class ValidationError(Exception):
"""验证错误异常"""
def __init__(self, message, field=None):
self.message = message
self.field = field
super().__init__(self.message)
def __str__(self):
if self.field:
return f"ValidationError: {self.message} (字段: {self.field})"
return f"ValidationError: {self.message}"
class DatabaseError(Exception):
"""数据库错误异常"""
pass
# 使用自定义异常
def validate_user(user_data):
if not user_data.get('name'):
raise ValidationError('姓名不能为空', 'name')
if not user_data.get('email'):
raise ValidationError('邮箱不能为空', 'email')
if '@' not in user_data.get('email', ''):
raise ValidationError('邮箱格式不正确', 'email')
return True
try:
user = {
'name': 'Alice',
'email': 'alice@example.com'
}
validate_user(user)
print("用户验证成功")
except ValidationError as e:
print(f"验证错误:{e}")
try:
user = {
'name': 'Bob',
'email': 'bob.example.com' # 错误的邮箱格式
}
validate_user(user)
print("用户验证成功")
except ValidationError as e:
print(f"验证错误:{e}")输出:
用户验证成功
验证错误:ValidationError: 邮箱格式不正确 (字段: email)异常链
在 Python 3 中,可以使用 raise ... from ... 语句来创建异常链,保留原始异常信息。
示例
python
# 异常链
try:
x = 10 / 0
except ZeroDivisionError as e:
raise ValueError("计算错误") from e输出:
Traceback (most recent call last):
File "example.py", line 3, in <module>
x = 10 / 0
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "example.py", line 5, in <module>
raise ValueError("计算错误") from e
ValueError: 计算错误异常处理的最佳实践
1. 只捕获必要的异常
python
# 不好的做法:捕获所有异常
try:
x = 10 / 0
except:
print("发生错误")
# 好的做法:只捕获特定异常
try:
x = 10 / 0
except ZeroDivisionError:
print("错误:除以零")2. 使用具体的异常类型
python
# 不好的做法:使用过于宽泛的异常
try:
x = int(input("请输入一个数字:"))
except Exception as e:
print(f"错误:{e}")
# 好的做法:使用具体的异常类型
try:
x = int(input("请输入一个数字:"))
except ValueError:
print("错误:请输入有效的数字")3. 提供有意义的错误信息
python
# 不好的做法:错误信息不明确
try:
with open("nonexistent.txt", "r") as f:
content = f.read()
except FileNotFoundError:
print("错误:文件不存在")
# 好的做法:提供具体的错误信息
try:
filename = "nonexistent.txt"
with open(filename, "r") as f:
content = f.read()
except FileNotFoundError:
print(f"错误:文件 '{filename}' 不存在")4. 使用 finally 子句清理资源
python
# 不好的做法:没有清理资源
f = open("example.txt", "r")
try:
content = f.read()
print(content)
except FileNotFoundError:
print("错误:文件不存在")
# 如果发生异常,文件可能不会被关闭
f.close()
# 好的做法:使用 finally 子句
f = None
try:
f = open("example.txt", "r")
content = f.read()
print(content)
except FileNotFoundError:
print("错误:文件不存在")
finally:
if f and not f.closed:
f.close()
print("文件已关闭")
# 更好的做法:使用 with 语句
with open("example.txt", "r") as f:
content = f.read()
print(content)
# with 语句会自动关闭文件5. 避免在 except 块中使用 pass
python
# 不好的做法:使用 pass 忽略异常
try:
x = 10 / 0
except ZeroDivisionError:
pass # 忽略异常
# 好的做法:至少记录异常
try:
x = 10 / 0
except ZeroDivisionError:
print("警告:除以零,结果设置为 0")
x = 06. 合理使用 else 子句
python
# 好的做法:使用 else 子句分离正常逻辑和异常处理
try:
x = int(input("请输入一个数字:"))
except ValueError:
print("错误:请输入有效的数字")
else:
print(f"你输入的数字是:{x}")
# 其他正常逻辑...实际应用示例
示例 1:文件操作异常处理
python
# 文件操作异常处理
def read_file(filename):
"""读取文件内容"""
try:
with open(filename, 'r', encoding='utf-8') as f:
content = f.read()
return content
except FileNotFoundError:
print(f"错误:文件 '{filename}' 不存在")
return None
except UnicodeDecodeError:
print(f"错误:文件 '{filename}' 编码错误")
return None
except Exception as e:
print(f"错误:读取文件时发生未知错误:{e}")
return None
# 测试
content = read_file("example.txt")
if content:
print(f"文件内容:{content}")
else:
print("无法读取文件")示例 2:网络请求异常处理
python
# 网络请求异常处理
import requests
def get_data(url):
"""获取网络数据"""
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # 检查 HTTP 状态码
return response.json()
except requests.exceptions.Timeout:
print("错误:请求超时")
return None
except requests.exceptions.HTTPError as e:
print(f"错误:HTTP 错误:{e}")
return None
except requests.exceptions.ConnectionError:
print("错误:连接错误")
return None
except Exception as e:
print(f"错误:发生未知错误:{e}")
return None
# 测试
# data = get_data("https://api.example.com/data")
# if data:
# print(f"获取的数据:{data}")
# else:
# print("无法获取数据")示例 3:数据库操作异常处理
python
# 数据库操作异常处理
import sqlite3
def query_database(db_path, query, params=()):
"""查询数据库"""
conn = None
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute(query, params)
result = cursor.fetchall()
conn.commit()
return result
except sqlite3.Error as e:
print(f"错误:数据库操作失败:{e}")
if conn:
conn.rollback()
return None
finally:
if conn:
conn.close()
# 测试
# result = query_database("example.db", "SELECT * FROM users WHERE age > ?", (18,))
# if result:
# print(f"查询结果:{result}")
# else:
# print("查询失败")示例 4:用户输入验证
python
# 用户输入验证
def get_valid_integer(prompt, min_value=None, max_value=None):
"""获取有效的整数输入"""
while True:
try:
value = int(input(prompt))
if min_value is not None and value < min_value:
print(f"错误:值不能小于 {min_value}")
continue
if max_value is not None and value > max_value:
print(f"错误:值不能大于 {max_value}")
continue
return value
except ValueError:
print("错误:请输入有效的整数")
continue
# 测试
age = get_valid_integer("请输入你的年龄:", 0, 120)
print(f"你的年龄是:{age}")
score = get_valid_integer("请输入你的分数:", 0, 100)
print(f"你的分数是:{score}")示例 5:自定义异常处理装饰器
python
# 自定义异常处理装饰器
def exception_handler(func):
"""异常处理装饰器"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"错误:{func.__name__} 函数执行失败:{e}")
return None
return wrapper
@exception_handler
def divide(a, b):
return a / b
@exception_handler
def get_user(user_id):
users = {1: "Alice", 2: "Bob", 3: "Charlie"}
return users[user_id]
# 测试
result = divide(10, 2)
print(f"10 / 2 = {result}")
result = divide(10, 0)
print(f"10 / 0 = {result}")
result = get_user(1)
print(f"用户 1:{result}")
result = get_user(4) # 不存在的用户 ID
print(f"用户 4:{result}")调试技巧
1. 使用 print 语句
python
# 使用 print 语句调试
def add(a, b):
print(f"调试:a = {a}, b = {b}")
result = a + b
print(f"调试:result = {result}")
return result
add(1, 2)2. 使用 assert 语句
python
# 使用 assert 语句调试
def divide(a, b):
assert b != 0, "除数不能为零"
return a / b
divide(10, 2)
divide(10, 0) # 会引发 AssertionError3. 使用 logging 模块
python
# 使用 logging 模块调试
import logging
# 配置 logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
def add(a, b):
logging.debug(f"a = {a}, b = {b}")
result = a + b
logging.debug(f"result = {result}")
return result
add(1, 2)4. 使用 pdb 调试器
python
# 使用 pdb 调试器
import pdb
def add(a, b):
pdb.set_trace() # 设置断点
result = a + b
return result
add(1, 2)总结
Python 中的错误和异常处理是编写健壮程序的重要组成部分。本章节介绍了:
- 错误与异常的区别:错误是语法或逻辑错误,异常是运行时事件。
- 常见异常类型:如 SyntaxError、TypeError、ValueError 等。
- 异常处理语句:try-except-else-finally。
- 抛出异常:使用 raise 语句。
- 自定义异常:继承 Exception 类。
- 异常链:使用 raise ... from ... 语句。
- 异常处理的最佳实践:只捕获必要的异常、提供有意义的错误信息等。
- 实际应用示例:文件操作、网络请求、数据库操作等。
- 调试技巧:使用 print、assert、logging 和 pdb。
通过合理的异常处理,可以使程序更加健壮,提高用户体验,并便于调试和维护。