调试

Python 调试技术完全指南:从 print 到专业工具

写代码很少一次就完美运行——在 Python 开发中,一次跑通且完全没 bug 的概率可能低于 1%。有些 bug 很友好,错误信息会直接告诉你行号和原因,比如 IndexError: list index out of range。但更多 bug 藏得很深,必须潜入程序的运行状态,去查看变量的具体变化、调用链的逻辑走向。因此,掌握一套系统的调试方法,是每个 Python 开发者的必修课。


调试方法概览

我们从最基础、最万能的方法讲起,逐步过渡到生产级和专业工具级的方案。

1. print() 打印调试法:入门级但不可替代

适用场景:刚写好的小片段代码、临时验证某个变量的值、快速定位逻辑断点(比如循环里第几次迭代出问题)。

代码示例

def calculate_div(s: str) -> float:
    n = int(s)
    print(f'[DEBUG] 当前计算的n值: {n}')  # f-string 一目了然
    return 10 / n

def main():
    calculate_div('0')

if __name__ == '__main__':
    main()

执行结果

[DEBUG] 当前计算的n值: 0
Traceback (most recent call last):
  ...
ZeroDivisionError: integer division or modulo by zero

优缺点总结

  • 优点:零学习成本,任何环境(哪怕只有终端 + 记事本)都能用。
  • 缺点:会污染业务代码,留下大量无用输出,容易忘记删除,影响正式版程序的可读性和性能。

2. assert 断言调试法:轻量级的约束检查

适用场景:在代码里添加“隐含前提”的验证——比如函数传入的参数必须满足什么条件,中间计算的结果不能出现非预期情况。如果断言不通过,程序直接终止报错,而不是带着错误继续跑,造成更难定位的问题。

代码示例

def calculate_div(s: str) -> float:
    n = int(s)
    # 断言:n 不能为 0,否则抛出带自定义信息的 AssertionError
    assert n != 0, f'calculate_div 的参数转换后不能为 0,传入的原始值是: {s}'
    return 10 / n

def main():
    calculate_div('0')

if __name__ == '__main__':
    main()

执行结果

Traceback (most recent call last):
  ...
AssertionError: calculate_div 的参数转换后不能为 0,传入的原始值是: 0

生产环境注意事项

断言是可以全局关闭的!用 -O(大写字母 O,不是数字 0)参数运行 Python 脚本时,所有 assert 语句都会被跳过,不会执行也不会报错:

python -O your_script.py

优缺点总结

  • 优点:比 print() 更规范,不需要手动删除,不污染生产代码的输出。
  • 缺点:大量使用仍会占用业务代码行数;关闭后如果依赖断言的约束会出问题,生产环境尽量不用。

3. logging 日志调试法:生产级调试的首选

适用场景:从开发阶段到测试、再到生产环境的全流程通用方案——既能查看调试信息,又能记录测试和生产中的关键数据,不会污染业务逻辑,还能灵活控制输出。

基础配置(输出到终端)

import logging

# 一次性配置(建议放在程序入口文件,只配置一次)
logging.basicConfig(
    level=logging.INFO,                            # 设置最低输出级别:INFO 及以上才会打印
    format='%(asctime)s - %(levelname)s - %(message)s'  # 自定义输出格式
)

def calculate_div(s: str) -> float:
    n = int(s)
    logging.info(f'当前计算的n值: {n}')   # INFO 级别记录常规信息
    return 10 / n

if __name__ == '__main__':
    s = input('请输入一个数字字符串: ')
    try:
        result = calculate_div(s)
        logging.info(f'计算结果: {result}')
    except ZeroDivisionError:
        logging.error('除数不能为 0!')         # ERROR 级别记录严重问题
    except ValueError:
        logging.warning('请输入有效的数字字符串!')  # WARNING 级别记录意外但不致命的情况

高级配置(输出到文件)

如果想把日志保存下来供后续分析,可以加上 filenamefilemode 参数:

import logging

logging.basicConfig(
    level=logging.DEBUG,                # 开发阶段可以设为 DEBUG,查看最详细的信息
    filename='app_debug.log',           # 日志文件名
    filemode='w',                       # w 是覆盖写,a 是追加写
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

常用日志级别(从低到高)

级别含义适用场景
DEBUG最详细的诊断信息开发阶段定位复杂问题
INFO确认程序按预期运行的信息记录关键业务流程的节点
WARNING意外但不影响程序运行的情况用户输入不规范、临时网络波动
ERROR导致某个功能无法完成的问题接口调用失败、文件读写错误
CRITICAL导致整个程序崩溃的问题数据库连接完全断开

优缺点总结

  • 优点:全流程通用、可持久化、可灵活配置(级别、格式、输出位置)、生产环境友好。
  • 缺点:比前两种方法多一点点配置,但这配置非常值得。

4. pdb 命令行调试器:无 IDE 时的硬核工具

适用场景:服务器上没有 IDE / 编辑器,只能通过终端调试;或者想体验最底层的 Python 调试逻辑。

方法一:直接以 pdb 模式运行脚本

运行脚本时加上 -m pdb 参数,程序会在第一行代码暂停,等待你的命令:

python -m pdb your_script.py

方法二:在代码里嵌入断点

更常用的是在你怀疑出问题的前一行插入 pdb.set_trace(),程序运行到这里就会自动暂停:

import pdb

def calculate_div(s: str) -> float:
    n = int(s)
    pdb.set_trace()  # 插入断点
    return 10 / n

if __name__ == '__main__':
    calculate_div('0')

常用 pdb 命令(记住这几个就够了)

命令完整写法作用
llist查看当前位置附近的 11 行代码
nnext执行下一行(不进入函数内部)
sstep执行下一行(如果是函数则进入)
pprint打印某个变量的值,比如 p n
ccontinue继续执行直到下一个断点或结束
qquit退出调试,终止程序

调试示例

> /path/to/your_script.py(6)calculate_div()
-> return 10 / n
(Pdb) p n
0
(Pdb) c
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

5. IDE 集成调试:最顺手的日常方案

适用场景:日常本地开发——图形化界面、断点管理、变量监视、调用栈查看,这些功能组合起来,效率比命令行高太多。

主流工具推荐

  1. Visual Studio Code (VS Code)

    • 免费、轻量、插件生态丰富
    • 安装官方的「Python」扩展即可使用
    • 支持条件断点、数据断点、远程调试
    • 下载地址:https://code.visualstudio.com/
  2. PyCharm

    • 专业 Python IDE,对大型项目支持最好
    • 有免费的社区版和付费的专业版
    • 调试功能最全面,还有代码智能提示
    • 下载地址:https://www.jetbrains.com/pycharm/

IDE 调试的核心功能

  • 打断点:在代码行号左边点击即可,红色圆点表示断点
  • 单步调试:和 pdb 的 s / n 类似,但有按钮点击更方便
  • 变量监视:右侧可以看到所有当前作用域的变量值,也可以自己添加要监视的变量
  • 调用栈查看:可以看到当前代码是被哪一层函数调用的,快速回溯问题来源

调试策略建议

根据不同的开发阶段和环境,选择合适的工具组合:

  1. 本地日常开发:优先用 VS Code / PyCharm 的图形化调试
  2. 临时验证小逻辑:用 print() 快速试错
  3. 代码合并前 / 测试环境:用 logging 记录关键节点信息,关闭 print() 和调试用的 assert
  4. 生产环境排查问题:只用 logging,把级别设成 WARNINGERROR,避免产生大量垃圾日志;如需深入调试,可临时加 pdb.set_trace(),但用完一定要删除
  5. 服务器无 IDE 紧急排查:用 pdb 命令行调试

终极建议

虽然各种调试工具都有自己的优势,但 logging 是唯一能从开发用到生产、且几乎不影响正式版程序的方案。建议在你的 Python 项目里:

  1. 统一配置 logging 的格式和级别
  2. 合理规划日志内容(不要太细,也不要太粗)
  3. 建立简单的日志分析机制(比如定期清理日志、用 grep 过滤关键错误)

掌握这些调试技术,你就能快速高效地解决 Python 程序里绝大多数的问题!