如何逐步通过Python代码来帮助调试问题?

在Java/ c#中,您可以很容易地逐级检查代码以跟踪可能出错的地方,而IDE使这个过程对用户非常友好。

你能以类似的方式跟踪python代码吗?

382252 次浏览

在python中有一个叫做'pdb'的模块。在python脚本的顶部

import pdb
pdb.set_trace()

然后进入调试模式。您可以使用's'步进,'n'跟随下一行,类似于使用'gdb'调试器。

是的!有一个名为pdb的Python调试器就是用来做这件事的!

你可以使用pdb myscript.pypython -m pdb myscript.py通过pdb启动Python程序。

然后可以发出一些命令,这些命令都记录在pdb页面上。

要记住的一些有用的方法是:

  • b:设置断点
  • c:继续调试,直到遇到断点
  • s:通过代码的步骤
  • n:转到下一行代码
  • l:列出当前文件的源代码(默认值:11行,包括正在执行的行)
  • u:向上导航堆栈帧
  • d:向下导航堆栈帧
  • p:在当前上下文中打印表达式的值

如果你不想使用命令行调试器,一些ide,如Pydev中翼IDEPyCharm有一个GUI调试器。Wing和PyCharm都是商业产品,但Wing有一个免费的“个人”版本,PyCharm有一个免费的社区版本。

如果你想要一个集成调试器的IDE,试试PyScripter

如果你来自Java/ c#背景,我猜你最好的选择是使用EclipsePydev中。这为您提供了一个内置调试器的功能齐全的IDE。我也用django。

通过python代码进行编程步进和跟踪也是可能的(而且很简单!)查看sys.settrace ()文档了解更多细节。在这里也是一个入门教程。

https://wiki.python.org/moin/PythonDebuggingTools

Pudb是PDB的一个很好的替代品

使用Python交互式调试器'pdb'

第一步是使Python解释器进入调试模式。

A.从命令行

最直接的方式,从命令行运行,python解释器

$ python -m pdb scriptName.py
> .../pdb_script.py(7)<module>()
-> """
(Pdb)

B.解释器内部

在开发模块的早期版本时,并更迭代地进行实验。

$ python
Python 2.7 (r27:82508, Jul  3 2010, 21:12:11)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb_script
>>> import pdb
>>> pdb.run('pdb_script.MyObj(5).go()')
> <string>(1)<module>()
(Pdb)

C.从你的程序内部

对于大项目和长时间运行的模块,可以从程序内部使用启动调试 导入pdbset_trace() 像这样:

#!/usr/bin/env python
# encoding: utf-8
#


import pdb


class MyObj(object):
count = 5
def __init__(self):
self.count= 9


def go(self):
for i in range(self.count):
pdb.set_trace()
print i
return


if __name__ == '__main__':
MyObj(5).go()

逐步调试进入更内部

  1. " n " (next)执行下一个语句…

  2. 重复上一个调试命令,输入ENTER .

  3. " q " (quit)

  4. 用" p "打印变量的值(print)

    一)便士 < / p >

  5. " c " 关闭(Pdb)提示符…(继续)

  6. " l " (list)

  7. " s " (step into)

  8. " r " (return)

  9. 指定一个新值

    一)b = " b " < / p >

  10. 设置断点

    一)打破linenumber

    b) 打破functionname

    c) 文件名:linenumber < / p >

  11. < p >临时断点

    一)tbreak linenumber < / p >

  12. < p >条件断点

    一)换行数,条件 < / p >

注意:**所有这些命令都应该在**pdb中执行

更深入的了解请参考:-

< a href = " https://pymotw.com/2/pdb/ " rel = " noreferrer > https://pymotw.com/2/pdb/ < / >

https://pythonconquerstheuniverse.wordpress.com/2009/09/10/debugging-in-python/

PyCharm是一个包含调试器的Python IDE。观看这段YouTube视频,了解如何使用它来逐步执行代码:

PyCharm教程-使用PyCharm调试python代码(调试从6:34开始)

截图

注:PyCharm是一个商业产品,但该公司确实向学生和教师提供了免费的许可证,以及“轻量级”。社区版本是免费和开源的。

从Python 3.7开始,你可以使用内置函数breakpoint()进入调试器:

foo()
breakpoint()  # drop into the debugger at this point
bar()

默认情况下,breakpoint()将导入pdb并调用pdb.set_trace()。但是,你可以通过sys.breakpointhook()和使用环境变量PYTHONBREAKPOINT来控制调试行为。

更多信息请参见PEP 553

ipdb (IPython调试器)

ipdb向pdb添加了IPython功能,提供了以下巨大的改进:

  • 选项卡完成
  • 显示更多的上下文行
  • 语法高亮显示

就像pdg一样,如果与GDB相比,ipdb还远远不够完美和完全初级,但它已经比pdb有了巨大的改进。

用法类似于pdb,只需安装它:

python3 -m pip install --user ipdb

然后添加到你想要进行步进调试的行:

__import__('ipdb').set_trace(context=21)

你可能想从你的编辑器中添加一个快捷方式,例如,对于Vim snipmate I有:

snippet ipd
__import__('ipdb').set_trace(context=21)

所以我可以只输入ipd<tab>,它扩展到断点。然后使用dd删除它很容易,因为所有内容都包含在一行中。

context=21增加上下文行数,如我如何能使ipdb显示更多行上下文,而调试?所述

或者,你也可以从一开始调试程序:

ipdb3 main.py

但你通常不想这样做,因为:

  • 在Python读取这些行时,你必须遍历所有函数和类的定义
  • 我不知道如何在不入侵ipdb的情况下设置上下文大小。补丁允许它:https://github.com/gotcha/ipdb/pull/155

或者,就像在raw pdb 3.2+中一样,你可以从命令行设置一些断点:

ipdb3 -c 'b 12' -c 'b myfunc' ~/test/a.py

尽管由于某些原因-c c被破坏了

python -m module调试已经在:如何调试Python模块运行Python -m从命令行?被要求,因为Python 3.7可以用:

python -m pdb -m my_module

与GDB相比,pdb和ipdb的特性严重缺失:

Ipdb具体烦恼:

目前存在breakpoint()方法,它取代了import pdb; pdb.set_trace()

它还有几个新特性,比如可能的环境变量。

Python的导师是一个为新手设计的在线单步调试器。你可以在编辑页面上输入代码,然后点击“可视化执行”。让它开始运行。

除其他外,它支持:

  • 隐藏变量,例如,隐藏名为x的变量,将其放在末尾:

    #pythontutor_hide: x
    
  • < p > 保存/共享

  • 一些其他语言,如Java, JS, Ruby, C, c++

然而,它也它不支持很多东西,例如:

  • 读取/写入文件-使用io.StringIOio.BytesIO代替:演示
  • 代码太大,运行时间太长,或者定义了太多的变量或对象
  • 命令行参数
  • 很多标准库模块,如argparse, csv, enum, html, os, sys, weakref…
  • Python 3.7 +

让我们看看在3.7+中breakpoint()可以为你做什么。

我已经安装了ipdbpdbpp,它们都是增强的调试器,通过

pip install pdbpp
pip install ipdb

我的测试脚本,真的没有做太多,只是调用breakpoint()

#test_188_breakpoint.py
myvars=dict(foo="bar")
print("before breakpoint()")
breakpoint()   # 👈
print(f"after breakpoint myvars={myvars}")




breakpoint()链接到PYTHONBREAKPOINT环境变量。

案例1:禁用断点()

你可以像往常一样通过bash设置变量

export PYTHONBREAKPOINT=0

这将关闭断点(),在那里它什么都不做(只要你没有修改sys.breakpointhook(),它超出了这个答案的范围)。

这是程序运行的样子:

(venv38) myuser@explore$ export PYTHONBREAKPOINT=0
(venv38) myuser@explore$ python test_188_breakpoint.py
before breakpoint()
after breakpoint myvars={'foo': 'bar'}
(venv38) myuser@explore$


没有停止,因为我禁用了断点。pdb.set_trace()不能做的事情😀😀😀!

情况2:使用默认的pdb行为:

现在,让我们取消设置PYTHONBREAKPOINT,这将使我们回到正常的启用断点行为(它只在0时禁用,而不是在空时禁用)。

(venv38) myuser@explore$ unset PYTHONBREAKPOINT
(venv38) myuser@explore$ python test_188_breakpoint.py
before breakpoint()
[0] > /Users/myuser/kds2/wk/explore/test_188_breakpoint.py(6)<module>()
-> print(f"after breakpoint myvars={myvars}")
(Pdb++) print("pdbpp replaces pdb because it was installed")
pdbpp replaces pdb because it was installed
(Pdb++) c
after breakpoint myvars={'foo': 'bar'}


它停止了,但我实际上得到了pdbpp,因为它在安装时完全取代了pdb。如果我卸载pdbpp,我将回到正常的pdb

注意:标准的pdb.set_trace()仍然会得到pdbpp

案例3:调用自定义调试器

但是让我们改为调用ipdb。这一次,我们不需要设置环境变量,而是可以使用bash仅为这一个命令设置它。

(venv38) myuser@explore$ PYTHONBREAKPOINT=ipdb.set_trace py test_188_breakpoint.py
before breakpoint()
> /Users/myuser/kds2/wk/explore/test_188_breakpoint.py(6)<module>()
5 breakpoint()
----> 6 print(f"after breakpoint myvars={myvars}")
7


ipdb> print("and now I invoked ipdb instead")
and now I invoked ipdb instead
ipdb> c
after breakpoint myvars={'foo': 'bar'}

本质上,当查看$PYTHONBREAKPOINT时,它所做的是:

from ipdb import set_trace  # function imported on the right-most `.`
set_trace()

同样,它比普通的pdb.set_trace()😀😀😀聪明得多

在实践中?我可能会选择调试器。

如果我总是想要ipdb,我会:

  • export它通过.profile或类似。
  • 逐个命令禁用,不修改正常值

示例(pytest和调试器通常会导致不愉快的夫妻):

(venv38) myuser@explore$ export PYTHONBREAKPOINT=ipdb.set_trace
(venv38) myuser@explore$ echo $PYTHONBREAKPOINT
ipdb.set_trace
(venv38) myuser@explore$ PYTHONBREAKPOINT=0 pytest test_188_breakpoint.py
=================================== test session starts ====================================
platform darwin -- Python 3.8.6, pytest-5.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /Users/myuser/kds2/wk/explore
plugins: celery-4.4.7, cov-2.10.0
collected 0 items


================================== no tests ran in 0.03s ===================================
(venv38) myuser@explore$ echo $PYTHONBREAKPOINT
ipdb.set_trace

注。

我在macos下使用bash,任何posix shell的行为基本相同。Windows,无论是powershell还是DOS,都可能有不同的功能,特别是在PYTHONBREAKPOINT=<some value> <some command>周围,只能为一个命令设置环境变量。

VSCode

如果你想使用IDE,这是PyCharm的一个很好的替代方案。

  1. 安装VSCode
  2. 安装Python扩展,如果它还没有安装
  3. 用Python代码创建一个文件mymodule.py
  4. 若要设置断点,请将鼠标悬停在行号上并单击红点,或按F9
  5. 点击F5开始调试,并选择Python文件

它将在断点处停止,你可以做你通常的调试工作,比如检查变量的值,可以在选项卡变量(通常在左边)或单击调试控制台(通常在底部终端旁边):

调试过程中显示调试控制台的VSCode截图

这个截图显示VSCodium

更多的信息