前言
- 操作:错误处理、异常捕获和调试测试
- 目的:确保程序质量和稳定性的重要方面
1. 错误
错误通常指的是程序中的语法错误和逻辑错误
1.1 语法错误
- 定义:代码不符合Python语言的语法标准,导致Python解释器无法正确解析代码
- 示例
# 缺少冒号,导致语法错误
if True
print("Hello,world!")
- 运行错误提示
D:\PycharmProjects\pythonProject.venv\Scripts\python.exe D:\PycharmProjects\pythonProject\test.py
File “D:\PycharmProjects\pythonProject\test.py”, line 2122
if True
^
SyntaxError: expected ‘:’Process finished with exit code 1
1.2 逻辑错误
- 定义:代码逻辑错误导致程序崩溃,通常是由于算法设计错误或对业务逻辑的理解有误导致
- 示例
# 除数为零
10 / 0 # ZeroDivisionError
- 运行错误提示
D:\PycharmProjects\pythonProject.venv\Scripts\python.exe D:\PycharmProjects\pythonProject\test.py
Traceback (most recent call last):
File “D:\PycharmProjects\pythonProject\test.py”, line 2131, in
10 / 0 # ZeroDivisionError
~^
ZeroDivisionError: division by zeroProcess finished with exit code 1
2. 异常
- 异常通常指在程序运行过程中出现的意外情况,导致程序无法正常执行
- 常见异常类型有 ZeroDevisionError、FileNotFoundError、IndexError、KeyError、ValueError、TypeError等
2.1 ZerDevisionError
- 当除数为零时会抛出异常
try:
result = 1 / 0
except ZeroDivisionError:
print("除数不能为零")
2.2 FileNotFoundError
- 尝试打开一个不存在的文件时会抛出该异常
try:
with open("nonexistent_file.txt","r") as file:
content = file.read()
except FileNotFoundError:
print("文件未找到")
2.3 IndexError
- 当尝试访问列表、元组等序列类型中不存在的索引时,会抛出该异常
my_list = [1,2,3]
try:
print(my_list[3])
except IndexError:
print("索引超出范围")
2.4 keyError
- 当尝试访问字典中不存在的键、或者在集合中使用不存在的键时,会抛出该异常
- 即尝试使用一个在映射对象中不存在的键来获取对应的值
my_dict = {'name': 'Alice', 'age': 25}
# 尝试访问不存在的键
try:
value = my_dict['city']
except KeyError as e:
print(f"捕获到 KeyError: {e}")
2.5 ValueError
- 传入的参数类型正确,但参数的值不符合要求
# 字符串'abc'无法转换为整数
num_str = "abc"
try:
num = int(num_str)
except ValueError as e:
print(f"捕获到 ValueError: {e}")
2.6 TypeError
- 当实际传入的参数类型与期望的不符时,会抛出该异常
# 字符串和整数是不同的数据类型,不能直接用 + 运算符进行相加操作
string_value = "Hello"
int_value = 123
try:
result = string_value + int_value
except TypeError as e:
print(f"捕获到 TypeError: {e}")
3. 异常处理方法
3.1 try-except 捕获异常
try:
num = int(input("请输入一个整数: "))
result = 10 / num
print(f"结果是: {result}")
except ValueError:
print("输入不是有效的整数。")
except ZeroDivisionError:
print("除数不能为零。")
3.2 else 和 finally 子句
- else:无异常时执行
- finally:无论是否异常,最后执行(常用于清理资源)
try:
file = open("data.txt","r")
except FileNotFoundError:
print("文件不存在!")
else:
print(file.read())
finally:
file.close() #确保文件关闭
4.抛出异常
4.1 使用 raise 主动抛出异常
def validate_age(age):
if age < 0:
raise ValueError("年龄不能为负数!")
return age
try:
validate_age(-5)
except ValueError as e:
print(e) #输出:年龄不能为负数!
4.2 重新抛出异常
try:
# process_data()
except ValueError:
print("处理数据时出错")
raise #重新抛出异常供上层处理
5. 自定义异常
- 通过继承 Exception 类创建自定义异常:
class InvalidEmailError(Exception):
"""邮箱格式无效异常"""
def __init__(self,email):
self.email = email
super().__init__(f"邮箱 {email} 格式无效!")
def send_email(email):
if "@" not in email:
raise InvalidEmailError(email)
#发送邮件逻辑...
try:
send_email("user.exampe.com")
except InvalidEmailError as e:
print(e)
6. 调试方法
- 调试是指在程序中查找和修复错误的过程
6.1 使用 print 调试
- 插入 print 打印语句,协助观察处处变量的值和程序的执行流程
def add(a,b):
print(f"输入的参数 a = {a},b = {b}")
result = a + b
print(f"计算结果:{result}")
return result
add(4,4)
6.2 使用 pdb 调试器
- pdb 是 Python 的标准调试器,可以在程序中设置断点,逐行执行代码,观察变量的值
- 启动调试:在代码中插入 pdb.set_trace()
import pdb
def divide(a,b):
pdb.set_trace() #断点
return a / b
divide(10,0)
- 常用命令
n(next):执行下一行
c(continue):继续执行到下一个断点
q(quit):退出调试
6.3 IDE 调试工具
- VS Code:设置断点,逐行调试
- PyCharm:图形化调试界面,支持变量监视
7. 单元测试
7.1 使用 unittest 框架
import unittest
def add(a,b):
return a + b
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2,3),5)
self.assertRaises(TypeError,add,"2",3)
if __name__ == "__main__":
unittest.main()
7.2 使用 pytest 框架
- pytest 是第三方测试框架,使用 assert 语句来验证函数的返回值
- 安装:pip install pytest
- 示例:
# test_math.py
def test_add():
assert add(2,3) == 5
def test_add_fail():
with pytest.raises(TypeError):
add("2",3)
# 运行测试:终端执行 pytest_math.py
8. 最佳实践
8.1 避免宽泛的 except
- 尽量捕获特定异常,防止隐藏潜在错误
# 不推荐
try:
risky_operation()
except: # 捕获所有异常
pass
# 推荐
try:
risky_operation()
except (ValueError,IOError) as e:
handle_erroe(e)
8.2 资源清理
- 使用 finally 或上下文管理器确保资源释放
with open("file.txt","r") as f:
content = f.read()
8.3 日志记录
- 使用 logging 模块记录异常信息
import logging
logging.basicConfig(filename="app.log",level=logging.ERROR)
try:
process()
except Exception as e:
logging.error("处理失败:%s",e,exc_info=True)
8.4 防御性编程
- 验证输入数据,减少异常触发可能
def safe_divide(a,b):
if b == 0:
return 0 # 或抛出特定异常
return a / b
9.常见问题与解决
9.1 异常被静默捕获
- 问题:except 块未处理异常或未记录日志
- 解决:至少打印异常信息或重新抛出
9.2 循环中的异常处理
- 场景:处理批量数据时,需跳过错误项继续执行
data = [1,2,"3",4]
results = []
for item in data:
try:
results.append(int(item))
except ValueError:
print(f"无法转换:{item}")
9.3 调试复杂代码
- 工具:使用 IDE 的调试功能,结合条件断点和变量监视
10 附
- Python 中的所有异常也是一种类,BaseException 是所有异常类的基类
- 使用 except 时,不但捕获该类型的异常信息,也包含其子类下的所有异常信息
- 常见的错误类型和继承关系:
BaseException
├── BaseExceptionGroup
├── GeneratorExit
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ExceptionGroup [BaseExceptionGroup]
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ ├── PythonFinalizationError
│ └── RecursionError
├── StopAsyncIteration
├── StopIteration
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── BytesWarning
├── DeprecationWarning
├── EncodingWarning
├── FutureWarning
├── ImportWarning
├── PendingDeprecationWarning
├── ResourceWarning
├── RuntimeWarning
├── SyntaxWarning
├── UnicodeWarning
└── UserWarning