顾乔芝士网

持续更新的前后端开发技术栈

如何像专业人士一样调试 Python 代码

在学习 Python 编程的过程中,写代码并不算难,真正让人崩溃的往往是——调试。 很多初学者调 Bug 的方式是:随便加几个 print(),看看屏幕输出,祈祷问题自己消失。结果信息越来越多,思路却越来越乱。

我也曾这样“撞大运式”调试,既低效又心累。直到后来,我逐渐总结出一套清晰、有逻辑的调试方法,不再靠运气,而是通过结构化思路和工具,快速定位问题。

今天,我想分享这套 实战经验,帮你从“盲猜式调试”转向“高效系统化调试”。


一、从错误信息入手:别再忽视 Traceback

很多人看到 Python 抛出的长长错误提示时,会选择跳过,甚至直接慌了。但事实上,这些信息就是最直接的线索。

例子:

def divide(a, b):
    return a / b
print(divide(10, 0))

运行结果是:

ZeroDivisionError: division by zero

Python 已经清楚地告诉你,问题在“除以零”。调试时,先看最后一行错误提示,因为它才是导致崩溃的根本原因。

适用场景

  • 程序直接报错时
  • 错误堆栈很长,不知道从哪里下手

常见误区

  • 只看第一行提示:其实应该先看最后一行
  • 忽视报错文件和行号:很多人明明 Python 已经给出位置,却依然满代码乱找

二、聪明地使用 print():精准,而不是乱打

print() 是大家最常用的调试方式,但大多数人用得很随意,打印的信息反而让问题更复杂。

更好的做法是:只打印能回答问题的内容

def process(numbers):
    total = 0
    for i, num in enumerate(numbers):
        total += num
        print(f"Step {i}: num={num}, total={total}")  
    return total

这种方式能让你清楚地看到每一步变量的变化过程。

适用场景

  • 小型函数、逻辑不复杂的调试
  • 想快速确认变量的中间值

常见误区

  • 打印无意义的内容(例如“here”“there”)
  • 一次性打印太多变量,结果屏幕满屏数据,反而没重点

三、用上 Python 自带的调试器 pdb

相比 print()pdb 更专业,可以让你在程序运行中“暂停”,随时查看内部状态。

示例:

import pdb

def calculate_area(length, width):
    pdb.set_trace()
    return length * width

print(calculate_area(5, 0))

运行到 set_trace() 时,程序会停下来等待你输入命令:

  • print(length) → 查看变量值
  • next → 单步运行
  • continue → 继续运行

适用场景

  • 逻辑较复杂,需要逐行观察代码执行过程
  • 想临时查看变量状态,而不是到处加 print()

常见误区

  • 只知道停下来,却不会输入命令
  • 随便在代码里加太多 set_trace(),结果流程一团糟

四、日志记录:替代满屏的 print()

随着项目规模变大,print() 会让控制台变成“垃圾场”。 这时就要用 logging

import logging
logging.basicConfig(level=logging.DEBUG)

def add(a, b):
    logging.debug(f"Adding {a} + {b}")
    return a + b

print(add(10, 20))

日志的优势在于:

  • 可以设置不同级别(DEBUG、INFO、ERROR)
  • 需要时过滤掉不重要的信息
  • 结构化输出,便于管理

适用场景

  • 中大型项目
  • 需要长期保留调试信息,或多人协作

常见误区

  • 仍然当作 print(),不设置级别
  • 日志全开 DEBUG,导致输出信息太多,失去意义

五、用 IDE 的断点调试:更直观的方式

VS Code、PyCharm 等 IDE 自带的调试工具,比 pdb 更友好。

操作很简单:在代码行号处点一下,设置一个断点,然后用“调试模式”运行。程序执行到这一行会暂停,你可以:

  • 鼠标悬停查看变量
  • 单步执行代码
  • 快速查看调用链

本质上,它就是 图形化的 pdb

适用场景

  • 不喜欢命令行操作的人
  • 需要直观、可视化的调试体验

常见误区

  • 以为复杂就不用,实际上 IDE 调试比 pdb 更容易上手
  • 只会打断点,却不会单步调试

六、从小处测试:不要一次跑完整个程序

新手常犯的错误是:写好一个大函数,一口气运行,报错了却不知道问题出在哪。

比如:

def process_file(file):
    with open(file) as f:
        data = f.read().splitlines()
        numbers = [int(x) for x in data]
        avg = sum(numbers) / len(numbers)
        print("Average:", avg)

如果直接运行,可能遇到各种问题:文件路径错了、数据格式不对、除零错误……

正确做法是拆小测试:

print(open("data.txt").read())                 
print(open("data.txt").read().splitlines())    
print([int(x) for x in ["1", "2", "3"]])      

这样能快速缩小问题范围。

适用场景

  • 处理文件、数据转换等容易出错的环节
  • 不确定输入数据是否符合预期时

常见误区

  • 一次跑完所有逻辑,报错后难以定位
  • 跳过基本验证,直接假设数据正确

七、用 try-except 预防错误

调试不仅是修复,更是预防。 例如用户输入不合法数据时,可以用 try-except 提前处理:

try:
    num = int(input("Enter number: "))
    print(10 / num)
except ZeroDivisionError:
    print("Oops, division by zero!")
except ValueError:
    print("Invalid input, please enter a number")

适用场景

  • 用户输入、外部文件、网络请求等不确定性很高的地方
  • 容易出现已知错误类型时

常见误区

  • 一股脑用 except: 捕获所有错误,反而掩盖问题
  • 错误处理写得模糊,导致后续排查更难

八、写单元测试:未来的自己会感谢现在的你

单元测试可能是最被忽视的调试方式。 但一旦写起来,你会发现它能大幅度减少 Bug。

import unittest

def add(a, b):
    return a + b

class TestMath(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(-1, 1), 0)

unittest.main()

提前写好测试,可以在上线前发现问题,而不是等到用户反馈。

适用场景

  • 需要长期维护的项目
  • 对稳定性要求较高的代码

常见误区

  • 只写功能,不写测试,结果 Bug 一直重复出现
  • 测试不全,只覆盖了“理想情况”,却忽略了边界情况

九、总结:调试是一门可以练习的技能

调试不是靠运气,而是靠方法。 记住这几点:

  • 看报错信息,从最后一行开始
  • print(),但要有针对性
  • 学会 pdb 或 IDE 调试
  • 用日志管理信息
  • 逐步测试,而不是一次跑全局
  • try-except 处理可预见错误
  • 写单元测试,提前防范 Bug

调试的过程,本质上就是缩小问题范围、精准锁定原因。掌握这些技巧后,你会发现调试不再焦虑,而是一次有条理的推理之旅。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言