首页 / 知识库 / 0基础入门-阅读资料 / 0基础-python入门到精通

第 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)自动处理。写好文档字符串,配合类型检查工具,代码质量会自然提升。