【python】错误处理、异常捕获、调试和单元测试详解

发布于:2025-03-06 ⋅ 阅读:(106) ⋅ 点赞:(0)


前言

  • 操作:错误处理、异常捕获和调试测试
  • 目的:确保程序质量和稳定性的重要方面

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 zero

Process 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

网站公告

今日签到

点亮在社区的每一天
去签到