文章目录▼CloseOpen
- 第一步:先把调试工具摸熟——别再用print当“万能钥匙”
- 先搞懂PyCharm的“调试三板斧”——图形化工具最适合新手
- 再学个“备份方案”——pdb命令行调试,服务器环境也能用
- 第二步:学会“设断点”——不是所有行都值得停
- 普通断点——解决“明确哪里可能错”的问题
- 条件断点——解决“循环/批量操作”的问题
- 异常断点——解决“不知道哪里错”的问题
- 第三步:跟着“调用栈”找根源——别只盯着报错的那一行
- 最后:调试的“小习惯”——让你少走弯路
- 新手调试Python源码,选PyCharm还是pdb好?
- 条件断点怎么设置?适合解决什么问题?
- 调用栈是什么?为什么能帮我找bug根源?
- 调试时总是暂停在没用的地方,怎么精准设断点?
- 调试完需要记什么?写bug日记有必要吗?
- Debug(绿色小虫子):开始调试,和普通运行(Run)的区别是,调试会执行到断点处暂停。
- Step Over(跳步,图标是两个叠加的箭头):执行当前行的代码,但不进入函数内部。比如你写了
result = add(1,2)
,用Step Over的话,直接算出result的值,不会跳到add函数里看细节。 - Step Into(步入,图标是箭头指向里面):进入当前行调用的函数内部。比如上面的add函数,用Step Into会跳转到add的定义处,一步步看它怎么计算1+2。
- Step Out(步出,图标是箭头指向外面):从当前函数退回到调用它的地方。比如你在add函数里调试,用Step Out会回到
result = add(1,2)
这一行,继续执行后面的代码。 - python -m pdb 脚本名.py:用pdb启动脚本。
- b 行号:在指定行设断点(比如b 10,就是在第10行设断点)。
- n(next):执行下一行(类似Step Over)。
- s(step):进入函数(类似Step Into)。
- c(continue):继续执行到下一个断点。
- p 变量名:打印变量的值(比如p result,就能看result当前是多少)。
- 调试前先想“预期状态”:比如你认为变量s应该是“hello”,那暂停后就先看s的值是不是“hello”,不对再找原因。
- 别轻易修改代码再运行:调试时发现问题,先别急着改代码——先确认“是不是这里的问题”,比如把变量改成预期值,看程序能不能继续运行,如果能,再改代码。
- 调试完记下来:我有个“bug日记”,把每次遇到的bug和解决方法记下来,比如“循环索引越界——检查range的范围”“变量为None——看函数有没有return”,下次遇到类似问题,直接翻日记就能解决。
我们不会讲复杂的理论,而是直接带你走通从0到1的全流程:比如PyCharm、pdb这些常用调试工具怎么快速上手?断点除了「点一下」,还有条件断点、异常断点这些「高效玩法」?遇到变量值异常时,怎么顺着调用栈「顺藤摸瓜」找到根源?甚至连「调试时怎么避免打断正常流程」这种细节技巧,都帮你列清楚了。
不用记命令、不用怕界面复杂,跟着文中的步骤一步步练,下次遇到bug时,你再也不用对着源码叹气——而是能像「代码侦探」一样,精准定位问题、快速修复。不管是自己写的小项目报错,还是读开源代码想搞懂逻辑,这些技巧都能让你「调试效率翻倍」。
一句话:这篇文章,帮你把「摸不着头脑的调试」变成「清清楚楚的流程」。
你有没有过写Python代码时,运行报错却找不到哪里错了?比如明明变量应该是10,结果打印出来是None;或者循环跑着跑着突然卡住,盯着代码看半小时都没头绪?我当初学调试时也这样——第一次用PyCharm的调试功能,点了断点却不知道接下来该点什么,变量窗口里的内容一堆,根本看不懂哪个有用。直到后来跟着公司的老程序员学了套“笨办法”,才发现调试不是“碰运气”,而是有明确的步骤能一步步揪出问题。今天就把这套我亲测有效的全流程技巧分享给你,不用记复杂命令,跟着做就能上手。
第一步:先把调试工具摸熟——别再用print当“万能钥匙”
我见过很多新手调试全靠print:每写几行就加个print,运行后看输出找问题。但print有个致命缺点——只能看到“某几个点”的状态,没法连续查看程序运行的过程。比如你要找循环里的变量变化,得加10个print,输出20行内容,眼睛都看花了。后来我换成调试工具,才发现“原来找bug可以这么快”。
先搞懂PyCharm的“调试三板斧”——图形化工具最适合新手
PyCharm是大部分Python开发者用的IDE,它的调试功能其实就几个核心按钮,我用大白话给你翻译:
我当初记不住这些名字,就直接理解成“跳步=不管函数里面,步入=钻进去看,步出=退出来”。第一次用的时候,我写了个计算斐波那契数列的函数,递归调用总是返回错误的值。用Step Into一步步钻进递归里,才发现Base Case(终止条件)写错了——应该是n<=1时返回n,我写成了n==1时返回1,n==0时没处理,结果第0项返回了None,导致后面全错。要是用print,我得打印每一步的n值,至少要10分钟才能找到问题,用调试工具5分钟就搞定了。
再学个“备份方案”——pdb命令行调试,服务器环境也能用
如果是在服务器上调试(没有图形界面),或者不想开IDE,那就用Python自带的pdb模块。它是命令行调试工具,需要记几个简单命令:
我之前在服务器上调试一个定时任务脚本,用PyCharm没法连,就用pdb。当时脚本运行到一半就退出,没有报错信息。我用pdb启动,设了个断点在循环开始的地方,然后用n一步步执行,每步都用p打印变量值,结果发现循环里的一个文件句柄没关闭,导致打开的文件太多,系统报错退出。要是没pdb,我根本不知道该怎么查服务器上的问题。
给你列个表格,对比下print和调试工具的区别,你就能明白为什么要换工具:
方法 | 优点 | 缺点 |
---|---|---|
简单,不用学工具 | 只能看局部点,输出多,效率低 | |
PyCharm调试 | 图形化直观,能连续看状态 | 依赖IDE,服务器环境不好用 |
pdb命令行 | 无依赖,适用于所有环境 | 需要记命令,不够直观 |
第二步:学会“设断点”——不是所有行都值得停
调试的核心是“暂停程序,查看状态”,但断点设错了,反而会浪费时间。我之前犯过一个傻:为了找循环里的问题,在循环开头设了个断点,结果每次都要手动点几十次“继续”才能到出错的那一次,差点把鼠标点坏。后来老程序员告诉我:“断点要‘精准打击’,别乱设。”
普通断点——解决“明确哪里可能错”的问题
普通断点就是点一下行号旁边的小红点,运行到那里就暂停。比如你怀疑某一行的变量赋值有问题,就在那行设断点,暂停后看变量窗口里的值对不对。我之前写一个用户注册的功能,密码加密后存到数据库,结果登录时总提示密码错误。我在加密密码的那行设了断点,暂停后看加密后的字符串,发现居然是“None”——原来加密函数忘了return结果,直接改了return就好。
条件断点——解决“循环/批量操作”的问题
如果问题出在循环的某一次(比如第100次循环才报错),总不能手动点100次吧?这时候条件断点就派上用场了。右键点击断点,选“Edit Breakpoint”,然后写条件(比如“i == 100”,i是循环变量),这样只有当条件满足时才会暂停。我之前处理一个批量导入Excel数据的脚本,第57行总是报错“索引越界”。我用条件断点设了“row_num == 57”,一下就找到了问题——原来Excel里第57行的某一列是空的,我没做判空处理,直接取了索引值。
异常断点——解决“不知道哪里错”的问题
如果不知道哪里会报错,但知道是某种类型的错误(比如TypeError、IndexError),那就用异常断点。PyCharm里点“Run”→“View Breakpoints”,然后勾选“Python Exceptions Breakpoint”下的对应异常(比如TypeError),这样运行时只要抛出这个异常,就会自动暂停在出错的行。我之前写一个爬虫脚本,有时候会报错“AttributeError: ‘NoneType’ object has no attribute ‘text’”,但不知道哪个标签没找到。用异常断点后,暂停在出错的行,发现是某篇文章的标题标签不存在,返回了None,加个判空就能解决。
这里要插个专业知识:为什么断点能精准定位?因为程序运行时是“动态”的,变量值、函数调用都是变化的,只有暂停了,才能看到“某一时刻”的具体状态——就像给跑步的人拍张照片,能看清他的姿势有没有问题。而print是“事后拍照”,只能看到几个瞬间,没法连续观察。
第三步:跟着“调用栈”找根源——别只盯着报错的那一行
你有没有遇到过这种情况:报错行是“s.strip()”,但s为什么是None?这时候光看报错行没用,得看“谁把s设成了None”——这就要用到调用栈(Call Stack)。
PyCharm的调试窗口里有个“Frames”面板,里面列的是函数调用的顺序:最上面的是当前执行的函数(比如func_b),下面的是调用它的函数(比如func_a),再下面是更外层的函数(比如main)。比如你在func_b里遇到错误,点一下func_a的帧,就能看到func_a里的变量值,看看它传给func_b的参数对不对。
我之前写一个购物车结算的功能,调用calculate_total(products)
计算总价,结果返回0。报错行是total += p.price
,但p.price是None。我看调用栈,发现products
列表里的某个商品是None——再往上看,是get_products(user_id)
函数返回了包含None的列表,而get_products
里查询数据库时,SQL语句漏了WHERE is_deleted = 0
,导致把已删除的商品查了出来(已删除商品的price是None)。要是只看报错的那一行,根本找不到问题的根源。
这里有个小技巧:优先看“局部变量”。调试窗口里的变量分“局部变量(Local)”和“全局变量(Global)”,局部变量是当前函数里定义的变量,全局变量是整个程序的变量。新手常盯着全局变量看,但其实当前函数的问题90%出在局部变量——比如你在func()里调试,就看func()里的变量,比如参数、临时变量,这些才是影响当前函数运行的关键。
最后:调试的“小习惯”——让你少走弯路
我现在调试时,有几个习惯一直保持着,亲测能提高效率:
其实调试没有那么难,无非是“用工具暂停→看状态→找和预期不一致的地方→修正”。你可以试着用我讲的步骤——先摸熟工具,再精准设断点,最后跟着调用栈找根源——下次遇到bug时,别着急,慢慢来,像侦探查案一样,每一步都有线索。
如果你按这些方法试了,欢迎回来告诉我效果!比如你用条件断点解决了什么问题?或者有没有遇到更奇葩的bug?咱们一起讨论~
新手调试Python源码,选PyCharm还是pdb好?
新手优先选PyCharm,它的调试功能是图形化的,按钮简单(比如绿色小虫子开始调试,跳步、步入按钮一看就懂),不用记命令,适合刚上手的人。
pdb是Python自带的命令行工具,没有界面依赖,适合服务器这种没图形界面的环境,要是你已经熟悉了调试逻辑,再学pdb当备份就行。
条件断点怎么设置?适合解决什么问题?
在PyCharm里右键点断点,选“Edit Breakpoint”,然后写条件(比如循环变量i==100),这样只有满足条件时才会暂停。
它特别适合循环或批量操作的场景,比如批量导入Excel时第57行报错,设条件“row_num==57”,直接定位到那一行,不用手动点几十次继续。
调用栈是什么?为什么能帮我找bug根源?
调用栈就是函数调用的顺序列表,最上面是当前正在执行的函数,下面是调用它的函数(比如func_b被func_a调用,func_a被main调用,栈里就是func_b→func_a→main)。
比如报错行是“s.strip()”但s是None,光看这行没用,点调用栈里的上一层函数,就能看到是谁把None传给了s,顺着找就能揪出根源——比如之前我遇到的加密函数没return结果,就是看调用栈发现的。
调试时总是暂停在没用的地方,怎么精准设断点?
别乱设断点,先想“哪里可能有问题”:怀疑某行变量赋值错了,就设普通断点;循环某一次报错,设条件断点(比如i==100);不知道哪里错但知道是某种异常(比如TypeError),就设异常断点(PyCharm里勾选对应的异常类型)。
比如我之前找循环里的索引越界问题,设了条件断点“i==57”,一下就定位到空值的行,比乱点省了好多时间。
调试完需要记什么?写bug日记有必要吗?
特别有用!我有个“bug日记”,把每次遇到的bug和解决方法记下来,比如“循环索引越界——检查range范围”“变量为None——看函数有没有return”。
下次遇到类似问题,直接翻日记就能解决,不用再重新想一遍,比如之前的爬虫AttributeError问题,翻日记就知道是标签不存在没判空,省了好多时间。