第 26 章 调试与代码质量
26.1 pdb / breakpoint() 调试器
breakpoint() — Python 3.7+ 推荐方式
def calculate(data):
result = []
for item in data:
value = item * 2
breakpoint() # 程序暂停,进入调试器
result.append(value)
return result
calculate([1, 2, 3])
pdb 常用命令
| 命令 | 说明 |
|---|---|
n (next) | 执行下一行 |
s (step) | 步入函数 |
c (continue) | 继续执行到下一个断点 |
r (return) | 执行到函数返回 |
p expr | 打印表达式的值 |
pp expr | 美化打印 |
l (list) | 显示当前代码 |
ll | 显示当前函数全部代码 |
w (where) | 显示调用栈 |
u (up) | 上移一个栈帧 |
d (down) | 下移一个栈帧 |
b N | 在第 N 行设置断点 |
cl (clear) | 清除断点 |
q (quit) | 退出调试器 |
h (help) | 帮助 |
# 条件断点
import pdb
def process(items):
for i, item in enumerate(items):
if i == 50:
pdb.set_trace() # 只在特定条件下暂停
do_something(item)
# 事后调试(程序崩溃后进入调试器)
python -m pdb script.py
# 或在代码中:
try:
buggy_code()
except Exception:
import pdb; pdb.post_mortem()
禁用 breakpoint
# 环境变量控制
PYTHONBREAKPOINT=0 python script.py # 禁用所有 breakpoint
PYTHONBREAKPOINT=ipdb.set_trace python script.py # 使用 ipdb
26.2 logging vs print 调试
# print 调试 — 临时用,记得删除
def calculate(x, y):
print(f"DEBUG: x={x}, y={y}") # 上线前必须删除!
result = x + y
print(f"DEBUG: result={result}")
return result
# logging 调试 — 生产级方案
import logging
logger = logging.getLogger(__name__)
def calculate(x, y):
logger.debug("计算 x=%s, y=%s", x, y)
result = x + y
logger.debug("结果: %s", result)
return result
# logging 的优势:
# 1. 可以通过配置控制输出级别,不需要删除代码
# 2. 可以输出到文件、网络等多种目标
# 3. 包含时间戳、模块名等上下文信息
# 4. 性能更好(级别不够时不格式化字符串)
快速调试技巧
# 使用 f-string 的 = 语法(Python 3.8+)
x = 42
y = [1, 2, 3]
print(f"{x = }, {y = }")
# 输出: x = 42, y = [1, 2, 3]
# 使用 icecream 库(第三方,但非常好用)
# pip install icecream
from icecream import ic
ic(x) # ic| x: 42
ic(add(1, 2)) # ic| add(1, 2): 3
26.3 assert 语句的正确使用
assert 用于检查程序内部不变量,不应用于验证用户输入:
# 正确用法 — 检查内部不变量
def binary_search(sorted_list, target):
assert sorted_list == sorted(sorted_list), "输入必须已排序"
# ...
# 正确 — 检查函数的前置/后置条件
def withdraw(account, amount):
assert amount > 0, "取款金额必须为正"
old_balance = account.balance
account.balance -= amount
assert account.balance == old_balance - amount, "余额计算错误"
# 错误 — 不要用于验证用户输入!
# assert 可以被 -O 选项禁用
def bad_example(user_input):
assert user_input != "", "输入不能为空" # 不安全!
# 正确 — 用显式的条件检查
def good_example(user_input):
if not user_input:
raise ValueError("输入不能为空")
# -O 选项会移除所有 assert 语句
python -O script.py # assert 不执行
python script.py # assert 正常执行
26.4 代码风格:PEP 8 / black / ruff
ruff — 最快的 Python linter + formatter
pip install ruff
# 检查代码
ruff check .
ruff check --fix . # 自动修复
# 格式化代码
ruff format .
# pyproject.toml
[tool.ruff]
line-length = 88
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP"]
ignore = ["E501"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
black — 固执己见的格式化工具
pip install black
black . # 格式化所有文件
black --check . # 只检查不修改
black --diff . # 显示差异
# pyproject.toml
[tool.black]
line-length = 88
target-version = ["py312"]
isort — 导入排序
pip install isort
isort .
ruff 已内置 isort 功能,推荐直接用 ruff。
26.5 文档字符串
Google Style(推荐)
def fetch_data(url: str, timeout: int = 30) -> dict:
"""从指定 URL 获取数据。
发送 HTTP GET 请求并返回解析后的 JSON 数据。
如果请求失败会自动重试最多 3 次。
Args:
url: 请求的 URL 地址。
timeout: 超时时间(秒),默认 30。
Returns:
解析后的 JSON 数据字典。
Raises:
ConnectionError: 网络连接失败。
ValueError: 响应不是有效的 JSON。
Examples:
>>> fetch_data("https://api.example.com/users")
{'users': [...]}
"""
pass
NumPy Style
def calculate_distance(point1, point2):
"""计算两点之间的欧几里得距离。
Parameters
----------
point1 : tuple of float
第一个点的坐标 (x, y)。
point2 : tuple of float
第二个点的坐标 (x, y)。
Returns
-------
float
两点之间的距离。
Examples
--------
>>> calculate_distance((0, 0), (3, 4))
5.0
"""
pass
类的文档字符串
class DataProcessor:
"""处理和转换数据的工具类。
支持多种数据格式的读取、清洗和转换操作。
Attributes:
source: 数据源路径。
format: 数据格式(csv/json/parquet)。
Examples:
>>> processor = DataProcessor("data.csv")
>>> result = processor.process()
"""
def __init__(self, source: str, format: str = "csv"):
"""初始化数据处理器。
Args:
source: 数据源文件路径。
format: 文件格式,支持 csv/json/parquet。
"""
self.source = source
self.format = format
26.6 类型检查与静态分析工具
# mypy — 静态类型检查
pip install mypy
mypy src/
# pyright — 更快的类型检查器
pip install pyright
pyright src/
# bandit — 安全漏洞扫描
pip install bandit
bandit -r src/
# vulture — 查找死代码
pip install vulture
vulture src/
综合配置
# pyproject.toml — 所有工具的统一配置
[tool.mypy]
python_version = "3.12"
strict = true
[tool.ruff]
line-length = 88
target-version = "py312"
[tool.ruff.lint]
select = ["ALL"]
ignore = ["D100", "D104"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --cov=src"
本章小结:调试和代码质量是专业开发的两大支柱。用
breakpoint()替代 print 调试,用logging替代 print 输出,用assert检查内部不变量。代码风格交给工具(ruff/black)自动处理。写好文档字符串,配合类型检查工具,代码质量会自然提升。