楼层: 首页/ 软件技术/ Python 基础/ 测试与调试
11

测试与调试

pytest, pdb, logging

写代码十分钟,调 bug 两小时——这不是段子。会写测试,bug 在本地就被拦住;会用日志,线上问题能复盘。这章学三样:pytest 写测试、pdb 打断点、logging 打日志。

pytest:最省心的测试框架

pytest 比自带的 unittest 简洁得多——不用写类,写个函数、用 assert 就行。装:pip install pytest。

# 文件:test_math.py def add(a, b): return a + b # 测试函数命名必须以 test_ 开头 def test_add(): assert add(1, 2) == 3 assert add(-1, 1) == 0 assert add(0, 0) == 0 # 异常测试:用 pytest.raises import pytest def test_raise(): with pytest.raises(ZeroDivisionError): 1 / 0 # 参数化测试:一次喂多组数据 @pytest.mark.parametrize("a,b,expected", [ (1, 2, 3), (0, 0, 0), (-1, 1, 0), ]) def test_add_params(a, b, expected): assert add(a, b) == expected

运行

pytest test_math.py -v # 看到一堆点(通过)或 F(失败)

调试:print / pdb / logging

# ① print 调试:最快但最土 print("这里的值是:", x) # ② pdb:命令行断点调试(Python 3.7+ 用 breakpoint()) def buggy(): x = 1 breakpoint() # 程序停在这,进入调试命令行 print(x + 1) # 进去后:n 下一步,p x 打印变量,c 继续,q 退出 # ③ logging:比 print 正规,能分级、能写文件 import logging logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logging.debug("细节,开发时才看") logging.info("正常流程") logging.warning("警告") logging.error("出错了")

性能分析:先测准,再优化

程序慢了,别凭感觉猜"肯定是网络慢"。先量化,再优化。两个标准库工具:timeit 测小段代码,cProfile 给整个程序画"谁最耗时"的账单。

timeit:精确测一小段代码跑多快

import timeit # 测"字符串拼接"和"列表 join"哪个快,各跑 10 万次 t1 = timeit.timeit("''.join(str(i) for i in range(100))", number=100000) t2 = timeit.timeit("''.join([str(i) for i in range(100)])", number=100000) print(f"生成器版: {t1:.3f}s 列表版: {t2:.3f}s") # 生成器版: 0.412s 列表版: 0.356s (数字随机器不同,结论是列表略快)

cProfile:给整个程序开账单

# 命令行直接跑:python -m cProfile -s cumtime your_script.py # -s cumtime 按累计耗时排序,一眼看到谁最费时间 # 也能在代码里这么用 import cProfile def slow_func(): return sum(i*i for i in range(100000)) cProfile.run("slow_func()") # ncalls tottime percall cumtime ... # 100001 0.012 0.000 0.028 slow_func <string>:1

怎么看账单:tottime 是函数自己花的时间(不含它调别人),cumtime 是累计时间(含它调的所有函数)。优化时先盯 cumtime 最高的那几个函数,别瞎改觉得慢的地方。

防坑:别在没 profile 之前就优化

Knuth 名言:"过早优化是万恶之源"。90% 的运行时间花在 10% 的代码上,但你猜不中那 10% 是哪。先 cProfile 找到真正的热点,再动手——否则只是把代码改得更难读,性能毫无变化。

练习:选工具(点开对答案)

问:想知道"解析这个 10MB JSON 慢在哪一步",用 timeit 还是 cProfile?
答:cProfile。它能列出整条调用链每一步的耗时,定位到具体哪个函数;timeit 只适合反复测一小段固定代码的平均耗时,定位不了"程序内部谁最慢"。