Python 中的异常
Python中的异常都派生于 BaseException docs.python.org/3/library/e…
异常处理try-except-else-finally
try:
a = 10 / 0
except ZeroDivisionError:
print("ZeroDivisionError")
except ValueError as e:
print("ZeroDivisionError", e)
else: # else 在没有任何异常的时候执行
print("No Error")
finally:
print("finally")
Assert 断言
断言失败,抛出 AssertionError
def assert_test(n):
n = int(n)
assert n > 0
return 10/n
assert assert_test(5)
assert assert_test(0) # AssertionError
运行时使用 -O xxx.py 可以跳过断言(可将assert看成pass)
(PythonProject) ➜ error git:(master) ✗ python -O TryExceptFinally.py
finally
logging
logging 模块是一个用于生成日志记录的标准库,通过 logging 模块,你可以灵活地设置多个日志处理器(Handler),分别将日志输出到不同的地方,比如控制台、文件、邮件等。常用的处理器有:
StreamHandler: 将日志输出到控制台。FileHandler: 将日志输出到文件。SMTPHandler: 将日志通过电子邮件发送。
import logging
# 设置基本配置
logging.basicConfig(level=logging.DEBUG, # 设置最低日志级别
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 设置日志格式
# 生成不同级别的日志信息
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
格式化
%(asctime)s: 日志时间戳%(name)s: 记录器的名称%(levelname)s: 日志级别%(message)s: 日志消息
将日志记录到文件中
logging.basicConfig(filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
自定义的日志器
日志器(Logger),创建自定义的日志器来进行更复杂的日志管理:
import logging
logger = logging.getLogger('my_logger')
# 设置日志级别
logger.setLevel(logging.DEBUG)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建文件处理器
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# 创建日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 生成日志
logger.debug('This is a debug message')
logger.info('This is an info message')
pdb Python Debugger
pdb(Python Debugger)是 Python 内建的调试工具,用于在 Python 程序中逐步执行代码、检查变量的状态,并发现和修复错误。pdb 允许你在代码中设置断点、查看堆栈跟踪以及修改代码状态。
启动 pdb 调试器
在你的代码中想要暂停的位置插入 pdb.set_trace(),这会启动调试器并进入交互式调试模式。
import pdb
def add(a, b):
result = a + b
pdb.set_trace() # 在此处设置断点
return result
add(3, 5)
进入调试模式
运行程序时,程序将在 pdb.set_trace() 处暂停并进入调试模式。你可以在调试模式下使用一系列命令来控制程序的执行。
常用的 pdb 调试命令
进入调试模式后,你可以使用以下命令来控制代码的执行:
n(next) : 执行当前行,并暂停在下一行代码。如果当前行调用了函数,n会执行完该函数并返回到当前行。s(step) : 执行当前行代码,并进入函数内部。如果当前行调用了函数,s会进入该函数。c(continue) : 继续执行代码,直到遇到下一个断点或者程序结束。l(list) : 显示当前行周围的代码,帮助你查看上下文。p(print) : 打印一个表达式的值。例如,p a会显示变量a的值。q(quit) : 退出调试器并终止程序。
使用命令行启动 pdb
bash
python -m pdb example.py
unittest 单元测试
unittest 是 Python 的标准库之一,用于编写和运行单元测试。单元测试是指对程序中的单个模块、函数或方法进行独立验证,以确保它们按预期工作。unittest 提供了一组功能来组织测试代码,检查函数结果,并在测试失败时提供详细的报告。
编写一个继承自 unittest.TestCase 的类,定义测试方法。在每个测试方法前,你通常会给方法加上 test_ 前缀,以便 unittest 框架能识别它是一个测试用例。
import unittest
# 被测试的代码
def add(a, b):
return a + b
def subtract(a, b):
return a - b
# 单元测试类
class TestMathFunctions(unittest.TestCase):
def test_add(self):
self.assertEqual(add(1, 2), 3) # 断言 1 + 2 == 3
def test_subtract(self):
self.assertEqual(subtract(5, 3), 2) # 断言 5 - 3 == 2
def test_add_negative(self):
self.assertEqual(add(-1, -1), -2) # 断言 -1 + (-1) == -2
# 如果当前是直接运行的主程序,则直接执行 unittest.main()
if __name__ == '__main__':
"""
unittest.main() 是 unittest 模块中的一个函数,用于自动发现并运行当前脚本中所有的测试用例。
自动搜索所有继承自 unittest.TestCase 的类;
自动运行所有以 test_ 开头的方法;
在测试结束后打印出测试结果。
"""
unittest.main()
断言方法
unittest 提供了一些常用的断言方法,用于验证函数或方法的输:
assertEqual(a, b): 检查a == b。assertNotEqual(a, b): 检查a != b。assertTrue(x): 检查x为True。assertFalse(x): 检查x为False。assertIsNone(x): 检查x为None。assertIsNotNone(x): 检查x不为None。assertRaises(exception, callable, *args, **kwargs): 检查是否抛出指定的异常。
运行测试
在终端中运行测试文件时,unittest.main() 会自动发现并执行类中所有以 test_ 开头的方法:
设置和清理
unittest 允许在每个测试之前和之后执行一些代码。这通常通过 setUp 和 tearDown 方法来实现:
setUp():在每个测试方法调用前执行。tearDown():在每个测试方法调用后执行。
class TestMathFunctions(unittest.TestCase):
def setUp(self):
print("Setting up for a test")
self.a = 1
self.b = 2
def tearDown(self):
print("Cleaning up after a test")
del self.a
del self.b
def test_add(self):
result = self.a + self.b
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
测试套件(Test Suites)
有时你可能想将多个测试组合在一起并一次性运行。可以通过 unittest.TestSuite 来创建一个测试套件。
import unittest
from dawn.error.unittest_test import TestMathFunctions
def suite():
testsuite = unittest.TestSuite()
testsuite.addTest(TestMathFunctions('test_add'))
testsuite.addTest(TestMathFunctions('test_subtract'))
return testsuite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
Docstring Tests 文档测试
Docstring Tests(文档字符串测试),是一种通过直接在文档字符串(docstring)中嵌入测试代码并通过 Python 的 doctest 模块来运行这些测试的方式。
在文档字符串中插入示例代码,并通过工具自动运行这些代码,确保代码和文档的同步性。它非常适合用来确保文档中的代码示例是可执行且正确的。
doctest 模块
doctest 模块是 Python 标准库中的一个模块,用于从文档字符串中提取并执行交互式代码片段,然后检查实际输出是否与文档中预期的输出一致。
import doctest
def add(a, b):
"""
返回两个数字的和。
示例:
>>> add(2, 3)
5
>>> add(-1, 1)
0
>>> add(0, 0)
0
"""
return a + b
# 如果你直接运行该脚本,它会自动执行文档测试。
if __name__ == "__main__":
doctest.testmod()
-
文档字符串:我们为函数
add写了文档字符串,文档字符串中包含了多个示例,这些示例以>>>开头,并且紧跟着期望的输出结果。 -
doctest.testmod():testmod()查找当前模块(即脚本中的所有函数和类)中的文档字符串,并执行其中的所有示例代码。它会验证文档字符串中的输出是否与实际代码的输出匹配。- 如果所有的输出匹配,
doctest会输出TestResults,并显示 "OK"。如果某些示例不匹配,它会显示错误和不匹配的部分。
-
启用详细模式
# 启用详细模式,显示测试过程中的更多信息
doctest.testmod(verbose=True)
- 忽略输出
如果你不关心某一部分输出(比如时间戳、机器生成的输出等),你可以使用 doctest.ELLIPSIS 来忽略部分输出。
import doctest
def example():
"""
返回一个时间戳。
示例:
>>> example()
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
"""
return 123 / 0
# 启用 ELLIPSIS 选项来忽略输出的变化部分
if __name__ == "__main__":
doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL)
- 运行特定模块
python -m doctest doctest_test.py