在所有编程练习中,如果没有方便的调试器,很难深入。Python 内置的调试器 `pdb` 已经成熟且功能强大,如果您知道如何使用它,它将为您提供很大帮助。在本教程中,我们将了解 `pdb` 能为您做些什么以及它的一些替代品。
在本教程中,您将学习
- 调试器能做什么
- 如何控制调试器
- Python 的 pdb 的局限性及其替代品
用我的新书 Python for Machine Learning 启动您的项目,包括分步教程和所有示例的 Python 源代码文件。
让我们开始吧。
Python 调试工具
摄影:Thomas Park。保留部分权利。
教程概述
本教程分为 4 个部分;它们是
- 运行调试器的概念
- 使用调试器的演练
- Visual Studio Code 中的调试器
- 在运行中的 Python 程序上使用 GDB
运行调试器的概念
调试器的目的是为您提供一个慢动作按钮,以控制程序的流程。它还允许您在特定时间冻结程序并检查其状态。
调试器下最简单的操作是单步执行代码。即一次运行一行代码,并等待您的确认后再进行下一行。我们希望以停止和启动的方式运行程序的原因是,这使我们能够检查逻辑和值,或验证算法。
对于较大的程序,我们可能不想从头开始单步执行代码,因为可能需要很长时间才能到达我们感兴趣的行。因此,调试器还提供了一个断点功能,当到达特定代码行时会启动。从那时起,我们可以逐行单步执行。
使用调试器的演练
让我们通过一个例子来看看如何使用调试器。下面是用于动画显示 粒子群优化 的 Python 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
import numpy as np import matplotlib.pyplot as plt 从 matplotlib.animation 导入 FuncAnimation def f(x,y): "目标函数" return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73) # 计算并绘制 [0,5]x[0,5] 范围内的 3D 函数 x, y = np.array(np.meshgrid(np.linspace(0,5,100), np.linspace(0,5,100))) z = f(x, y) # 找到全局最小值 x_min = x.ravel()[z.argmin()] y_min = y.ravel()[z.argmin()] # 算法的超参数 c1 = c2 = 0.1 w = 0.8 # 创建粒子 n_particles = 20 np.random.seed(100) X = np.random.rand(2, n_particles) * 5 V = np.random.randn(2, n_particles) * 0.1 # 初始化数据 pbest = X pbest_obj = f(X[0], X[1]) gbest = pbest[:, pbest_obj.argmin()] gbest_obj = pbest_obj.min() def update(): "执行粒子群优化一次迭代的函数" global V, X, pbest, pbest_obj, gbest, gbest_obj # 更新参数 r1, r2 = np.random.rand(2) V = w * V + c1*r1*(pbest - X) + c2*r2*(gbest.reshape(-1,1)-X) X = X + V obj = f(X[0], X[1]) pbest[:, (pbest_obj >= obj)] = X[:, (pbest_obj >= obj)] pbest_obj = np.array([pbest_obj, obj]).min(axis=0) gbest = pbest[:, pbest_obj.argmin()] gbest_obj = pbest_obj.min() # 设置基本图形:等高线图 fig, ax = plt.subplots(figsize=(8,6)) fig.set_tight_layout(True) img = ax.imshow(z, extent=[0, 5, 0, 5], origin='lower', cmap='viridis', alpha=0.5) fig.colorbar(img, ax=ax) ax.plot([x_min], [y_min], marker='x', markersize=5, color="white") contours = ax.contour(x, y, z, 10, colors='black', alpha=0.4) ax.clabel(contours, inline=True, fontsize=8, fmt="%.0f") pbest_plot = ax.scatter(pbest[0], pbest[1], marker='o', color='black', alpha=0.5) p_plot = ax.scatter(X[0], X[1], marker='o', color='blue', alpha=0.5) p_arrow = ax.quiver(X[0], X[1], V[0], V[1], color='blue', width=0.005, angles='xy', scale_units='xy', scale=1) gbest_plot = plt.scatter([gbest[0]], [gbest[1]], marker='*', s=100, color='black', alpha=0.4) ax.set_xlim([0,5]) ax.set_ylim([0,5]) def animate(i): "PSO 的步骤:算法更新并显示在图中" title = '迭代 {:02d}'.format(i) # 更新参数 update() # 设置图片 ax.set_title(title) pbest_plot.set_offsets(pbest.T) p_plot.set_offsets(X.T) p_arrow.set_offsets(X.T) p_arrow.set_UVC(V[0], V[1]) gbest_plot.set_offsets(gbest.reshape(1,-1)) return ax, pbest_plot, p_plot, p_arrow, gbest_plot anim = FuncAnimation(fig, animate, frames=list(range(1,50)), interval=500, blit=False, repeat=True) anim.save("PSO.gif", dpi=120, writer="imagemagick") print("PSO 找到的最佳解 f({})={}".format(gbest, gbest_obj)) print("全局最优解 f({})={}".format([x_min,y_min], f(x_min,y_min))) |
粒子群优化通过多次执行 update()
函数来完成。每次运行时,我们都更接近目标函数的最佳解。我们使用 matplotlib 的 FuncAnimation()
函数而不是循环来运行 update()
,这样我们就可以捕获每次迭代中粒子的位置。
假设此程序保存为 pso.py
。要在命令行中运行此程序,只需输入
1 |
python pso.py |
解决方案将打印到屏幕上,动画将保存为 PSO.gif
。但如果我们要使用 Python 调试器运行它,我们在命令行中输入以下内容
1 |
python -m pdb pso.py |
-m pdb
部分将加载 pdb
模块并让该模块为您执行文件 pso.py
。当您运行此命令时,您将看到如下所示的 pdb
提示符
1 2 3 |
> /Users/mlm/pso.py(1) -> import numpy as np (Pdb) |
在提示符下,您可以输入调试器命令。要显示支持的命令列表,我们可以使用 h
。要显示特定命令(例如 list
)的详细信息,我们可以使用 h list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
> /Users/mlm/pso.py(1) -> import numpy as np (Pdb) h 已记录的命令(输入 help ======================================== EOF c d h list q rv undisplay a cl debug help ll quit s unt alias clear disable ignore longlist r source until args commands display interact n restart step up b condition down j next return tbreak w break cont enable jump p retval u whatis bt continue exit l pp run unalias where 杂项帮助主题 ========================== exec pdb (Pdb) |
在调试会话开始时,我们从程序的第一行开始。通常,Python 程序会以几行 import
开始。我们可以使用 n
移动到下一行,或者使用 s
进入一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
> /Users/mlm/pso.py(1) -> import numpy as np (Pdb) n > /Users/mlm/pso.py(2) -> import matplotlib.pyplot as plt (Pdb) n > /Users/mlm/pso.py(3) -> from matplotlib.animation import FuncAnimation (Pdb) n > /Users/mlm/pso.py(5) -> def f(x,y) (Pdb) n > /Users/mlm/pso.py(10) -> x, y = np.array(np.meshgrid(np.linspace(0,5,100), np.linspace(0,5,100))) (Pdb) n > /Users/mlm/pso.py(11) -> z = f(x, y) (Pdb) s --调用-- > /Users/mlm/pso.py(5)f() -> def f(x,y) (Pdb) s > /Users/mlm/pso.py(7)f() -> return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73) (Pdb) s --返回-- > /Users/mlm/pso.py(7)f()->array([[17.25... 7.46457344]]) -> return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73) (Pdb) s > /Users/mlm/pso.py(14) -> x_min = x.ravel()[z.argmin()] (Pdb) |
在 pdb
中,代码行会在提示符之前打印。通常,我们更喜欢 n
命令,因为它执行该行代码并将流程移动到相同级别,而不会深入。当我们在调用函数的行(例如上述程序的第 11 行,运行 z = f(x, y)
)时,我们可以使用 s
进入该函数。
在上面的示例中,我们首先进入 f()
函数,然后再次单步执行计算,最后从函数中收集返回值并将其返回给调用该函数的行。我们看到,即使是像单行函数这样简单的函数,也需要多个 s
命令,因为从语句中找到函数、调用函数和返回函数都需要一个步骤。我们还可以看到,在函数体中,我们像调用函数一样调用了 np.sin()
,但调试器的 s
命令没有进入其中。这是因为 np.sin()
函数不是用 Python 实现的,而是用 C 实现的。pdb
不支持编译后的代码。
如果程序很长,多次使用 n
命令移动到我们感兴趣的位置会非常无聊。我们可以使用带有行号的 until
命令让调试器运行程序直到到达该行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
> /Users/mlm/pso.py(1) -> import numpy as np (Pdb) until 11 > /Users/mlm/pso.py(11) -> z = f(x, y) (Pdb) s --调用-- > /Users/mlm/pso.py(5)f() -> def f(x,y) (Pdb) s > /Users/mlm/pso.py(7)f() -> return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73) (Pdb) s --返回-- > /Users/mlm/pso.py(7)f()->array([[17.25... 7.46457344]]) -> return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73) (Pdb) s > /Users/mlm/pso.py(14) -> x_min = x.ravel()[z.argmin()] (Pdb) |
与 until
类似的命令是 return
,它将执行当前函数直到即将返回的点。您可以将其视为 until
,其行号等于当前函数的最后一行。until
命令是一次性的,这意味着它只会将您带到该行。如果您想在特定行每次运行时都停止,我们可以在其上设置断点。例如,如果我们对优化算法的每次迭代如何移动解决方案感兴趣,我们可以在应用更新后立即设置一个断点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
> /Users/mlm/pso.py(1) -> import numpy as np (Pdb) b 40 断点 1 位于 /Users/mlm/pso.py:40 (Pdb) c > /Users/mlm/pso.py(40)update() -> obj = f(X[0], X[1]) (Pdb) bt /usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/bdb.py(580)run() -> exec(cmd, globals, locals) /Users/mlm/pso.py(76) -> anim.save("PSO.gif", dpi=120, writer="imagemagick") /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1078)save() -> anim._init_draw() # 清除初始帧 /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1698)_init_draw() -> self._draw_frame(frame_data) /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1720)_draw_frame() -> self._drawn_artists = self._func(framedata, *self._args) /Users/mlm/pso.py(65)animate() -> update() > /Users/mlm/pso.py(40)update() -> obj = f(X[0], X[1]) (Pdb) p r1 0.8054505373292797 (Pdb) p r2 0.7543489945823536 (Pdb) p X array([[2.77550474, 1.60073607, 2.14133019, 4.11466522, 0.2445649 , 0.65149396, 3.24520628, 4.08804798, 0.89696478, 2.82703884, 4.42055413, 1.03681404, 0.95318658, 0.60737118, 1.17702652, 4.67551174, 3.95781321, 0.95077669, 4.08220292, 1.33330594], [2.07985611, 4.53702225, 3.81359193, 1.83427181, 0.87867832, 1.8423856 , 0.11392109, 1.2635162 , 3.84974582, 0.27397365, 2.86219806, 3.05406841, 0.64253831, 1.85730719, 0.26090638, 4.28053621, 4.71648133, 0.44101305, 4.14882396, 2.74620598]]) (Pdb) n > /Users/mlm/pso.py(41)update() -> pbest[:, (pbest_obj >= obj)] = X[:, (pbest_obj >= obj)] (Pdb) n > /Users/mlm/pso.py(42)update() -> pbest_obj = np.array([pbest_obj, obj]).min(axis=0) (Pdb) n > /Users/mlm/pso.py(43)update() -> gbest = pbest[:, pbest_obj.argmin()] (Pdb) n > /Users/mlm/pso.py(44)update() -> gbest_obj = pbest_obj.min() (Pdb) |
设置断点后,我们可以在满足触发条件之前让调试器运行程序。c
命令表示继续直到满足触发条件。在任何时候,我们都可以使用 bt
命令显示回溯以检查我们是如何到达该点的。我们还可以使用 p
命令打印变量(或表达式)以检查它们所持有的值。
实际上,我们可以设置带有条件的断点,这样它只有在满足条件时才会停止。以下将设置一个条件,即第一个随机数(r1
)大于 0.5
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(Pdb) b 40, r1 > 0.5 断点 1 位于 /Users/mlm/pso.py:40 (Pdb) c > /Users/mlm/pso.py(40)update() -> obj = f(X[0], X[1]) (Pdb) p r1, r2 (0.8054505373292797, 0.7543489945823536) (Pdb) c > /Users/mlm/pso.py(40)update() -> obj = f(X[0], X[1]) (Pdb) p r1, r2 (0.5404045753007164, 0.2967937508800147) (Pdb) |
实际上,我们也可以在调试时尝试操作变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
(Pdb) l 35 global V, X, pbest, pbest_obj, gbest, gbest_obj 36 # 更新参数 37 r1, r2 = np.random.rand(2) 38 V = w * V + c1*r1*(pbest - X) + c2*r2*(gbest.reshape(-1,1)-X) 39 X = X + V 40 B-> obj = f(X[0], X[1]) 41 pbest[:, (pbest_obj >= obj)] = X[:, (pbest_obj >= obj)] 42 pbest_obj = np.array([pbest_obj, obj]).min(axis=0) 43 gbest = pbest[:, pbest_obj.argmin()] 44 gbest_obj = pbest_obj.min() 45 (Pdb) p V array([[ 0.03742722, 0.20930531, 0.06273426, -0.1710678 , 0.33629384, 0.19506555, -0.10238065, -0.12707257, 0.28042122, -0.03250191, -0.14004886, 0.13224399, 0.16083673, 0.21198813, 0.17530208, -0.27665503, -0.15344393, 0.20079061, -0.10057509, 0.09128536], [-0.05034548, -0.27986224, -0.30725954, 0.11214169, 0.0934514 , 0.00335978, 0.20517519, 0.06308483, -0.22007053, 0.26176423, -0.12617228, -0.05676629, 0.18296986, -0.01669114, 0.18934933, -0.27623121, -0.32482898, 0.213894 , -0.34427909, -0.12058168]]) (Pdb) p r1, r2 (0.5404045753007164, 0.2967937508800147) (Pdb) r1 = 0.2 (Pdb) p r1, r2 (0.2, 0.2967937508800147) (Pdb) j 38 > /Users/mlm/pso.py(38)update() -> V = w * V + c1*r1*(pbest - X) + c2*r2*(gbest.reshape(-1,1)-X) (Pdb) n > /Users/mlm/pso.py(39)update() -> X = X + V (Pdb) p V array([[ 0.02680837, 0.16594979, 0.06350735, -0.15577623, 0.30737655, 0.19911613, -0.08242418, -0.12513798, 0.24939995, -0.02217463, -0.13474876, 0.14466204, 0.16661846, 0.21194543, 0.16952298, -0.24462505, -0.138997 , 0.19377154, -0.10699911, 0.10631063], [-0.03606147, -0.25128615, -0.26362411, 0.08163408, 0.09842085, 0.00765688, 0.19771385, 0.06597805, -0.20564599, 0.23113388, -0.0956787 , -0.07044121, 0.16637064, -0.00639259, 0.18245734, -0.25698717, -0.30336147, 0.19354112, -0.29904698, -0.08810355]]) (Pdb) |
在上面,我们使用 l
命令列出当前语句(由箭头 ->
标识)周围的代码。在列表中,我们还可以看到断点(用 B
标记)设置在第 40 行。由于我们可以看到 V
和 r1
的当前值,我们可以将 r1
从 0.54 修改为 0.2,并使用 j
(跳转)命令再次运行第 38 行的语句。我们看到,在使用 n
命令执行语句后,V
的值发生了变化。
如果我们使用断点并发现意外情况,则很可能是由调用堆栈中不同级别的问题引起的。调试器允许您导航到不同的级别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
(Pdb) bt /usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/bdb.py(580)run() -> exec(cmd, globals, locals) /Users/mlm/pso.py(76) -> anim.save("PSO.gif", dpi=120, writer="imagemagick") /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1091)save() -> anim._draw_next_frame(d, blit=False) /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1126)_draw_next_frame() -> self._draw_frame(framedata) /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1720)_draw_frame() -> self._drawn_artists = self._func(framedata, *self._args) /Users/mlm/pso.py(65)animate() -> update() > /Users/mlm/pso.py(39)update() -> X = X + V (Pdb) up > /Users/mlm/pso.py(65)animate() -> update() (Pdb) bt /usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/bdb.py(580)run() -> exec(cmd, globals, locals) /Users/mlm/pso.py(76) -> anim.save("PSO.gif", dpi=120, writer="imagemagick") /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1091)save() -> anim._draw_next_frame(d, blit=False) /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1126)_draw_next_frame() -> self._draw_frame(framedata) /usr/local/lib/python3.9/site-packages/matplotlib/animation.py(1720)_draw_frame() -> self._drawn_artists = self._func(framedata, *self._args) > /Users/mlm/pso.py(65)animate() -> update() /Users/mlm/pso.py(39)update() -> X = X + V (Pdb) l 60 61 def animate(i) 62 "PSO 的步骤:算法更新并显示在图中" 63 title = '迭代 {:02d}'.format(i) 64 # 更新参数 65 -> update() 66 # 设置图片 67 ax.set_title(title) 68 pbest_plot.set_offsets(pbest.T) 69 p_plot.set_offsets(X.T) 70 p_arrow.set_offsets(X.T) (Pdb) p title '迭代 02' (Pdb) |
在上面,第一个 bt
命令给出了我们在最底层帧(即调用堆栈的最深处)时的调用堆栈。我们可以看到我们即将执行语句 X = X + V
。然后 up
命令将我们的焦点向上移动一层调用堆栈,即运行 update()
函数的行(如我们看到的以 >
开头的行)。由于我们的焦点发生了变化,列表命令 l
将打印不同的代码片段,p
命令可以检查不同作用域中的变量。
以上涵盖了调试器中大多数有用的命令。如果我们想终止调试器(这也终止程序),我们可以使用 q
命令退出,如果您的终端支持,也可以按 Ctrl-D。
想要开始学习 Python 机器学习吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
Visual Studio Code 中的调试器
对 Python 编码更有信心
如果您不太习惯在命令行中运行调试器,您可以依靠 IDE 中的调试器。几乎所有 IDE 都会为您提供一些调试功能。例如,在 Visual Studio Code 中,您可以在“运行”菜单中启动调试器。
在运行中的 Python 程序上使用 GDB
Python 的 pdb
只适用于从头开始运行的程序。如果一个程序已经在运行但卡住了,我们无法使用 pdb 挂钩进去检查发生了什么。然而,GDB 的 Python 扩展可以做到这一点。
为了演示,我们考虑一个 GUI 应用程序。它会一直等待用户的操作,程序才能结束。因此,这是一个完美的示例,说明我们如何使用 gdb
挂钩到正在运行的进程中。下面的代码是一个使用 PyQt5 的“hello world”程序,它只是创建一个空窗口并等待用户关闭它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import sys 从 PyQt5.QtWidgets 导入 QApplication, QWidget, QMainWindow class Frame(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle("简单标题") self.resize(800,600) def main(): app = QApplication(sys.argv) frame = Frame() frame.show() sys.exit(app.exec_()) if __name__ == '__main__': main() |
我们将此程序保存为 simpleqt.py
,并在 Linux 的 X 窗口环境中运行它,使用以下命令
1 |
python simpleqt.py & |
末尾的 &
将使其在后台运行。现在我们可以使用 ps
命令检查其进程 ID
1 |
ps a | grep python |
1 2 3 |
... 3997 pts/1 Sl 0:00 python simpleqt.py ... |
ps
命令将在第一列中告诉您进程 ID。如果您的 gdb
安装了 Python 扩展,我们可以运行
1 |
gdb python 3997 |
它将带您进入 GDB 的提示符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git Copyright (C) 2021 Free Software Foundation, Inc. 许可证 GPLv3+:GNU GPL 版本 3 或更高版本 这是自由软件:您可以自由更改和重新分发它。 在法律允许的范围内,不提供任何担保。 输入 "show copying" 和 "show warranty" 查看详细信息。 此 GDB 配置为 "x86_64-linux-gnu"。 输入 "show configuration" 查看配置详细信息。 有关错误报告说明,请参阅 在以下网址查找 GDB 手册和其他文档资源 如需帮助,请输入 "help"。 输入 "apropos word" 以搜索与 "word" 相关的命令... 从 python 读取符号... 从 /usr/lib/debug/.build-id/f9/02f8a561c3abdb9c8d8c859d4243bd8c3f928f.debug 读取符号... 正在连接到程序:/usr/local/bin/python,进程 3997 [新 LWP 3998] [新 LWP 3999] [新 LWP 4001] [新 LWP 4002] [新 LWP 4003] [新 LWP 4004] [已启用线程调试,使用 libthread_db] 正在使用主机 libthread_db 库 "/lib/x86_64-linux-gnu/libthread_db.so.1"。 0x00007fb11b1c93ff in __GI___poll (fds=0x7fb110007220, nfds=3, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29 29 ../sysdeps/unix/sysv/linux/poll.c: 无此文件或目录。 (gdb) py-bt 回溯(最近一次调用在前) 文件 "/mnt/data/simpleqt.py", 第 16 行, main 函数中 sys.exit(app.exec_()) 文件 "/mnt/data/simpleqt.py", 第 19 行, main() (gdb) py-list 11 12 def main() 13 app = QApplication(sys.argv) 14 frame = Frame() 15 frame.show() >16 sys.exit(app.exec_()) 17 18 if __name__ == '__main__' 19 main() (gdb) |
GDB 应该是一个用于编译程序(通常是 C 或 C++)的调试器。Python 扩展允许您检查正在由 Python 解释器(用 C 编写)运行的代码(用 Python 编写)。在处理 Python 代码方面,它的功能不如 Python 的 PDB
丰富,但当您需要将其挂钩到正在运行的进程中时,它非常有用。
GDB 支持的命令有 py-list
、py-bt
、py-up
、py-down
和 py-print
。它们与 pdb
中没有 py-
前缀的相同命令相当。
如果您的 Python 代码使用从 C 编译的库(例如 numpy),并且您想调查其运行方式,GDB 会很有用。检查运行时调用堆栈以了解程序冻结的原因也很有帮助。但是,您可能很少需要使用 GDB 来调试您的机器学习项目。
进一步阅读
Python pdb
模块的文档位于
但 pdb
并不是唯一可用的调试器。一些第三方工具列在
对于带有 Python 扩展的 GDB,最好在 Linux 环境中使用。有关其用法的更多详细信息,请参阅以下内容
pdb
的命令接口受 GDB 影响。因此,我们可以从后者中学习一般调试程序的技术。一本关于如何使用调试器的优秀入门书是
- 使用 GDB、DDD 和 Eclipse 进行调试的艺术,作者 Norman Matloff (2008)
总结
在本教程中,您了解了 Python 的 pdb
的功能。
具体来说,你学到了:
pdb
能做什么以及如何使用它pdb
的局限性和替代品
在下一篇文章中,我们将看到 pdb
也是一个可以在 Python 程序中调用的 Python 函数。
对 GDB 的解释很好。我用过 PDB,但从没用过 GDB。谢谢!
感谢您的反馈和好评,肖恩!
sdfsdf
看看 ipdb,它提供了类似于 ipython 的接口。
感谢您的建议,Dale!
非常棒的文章。关于 gdb 用法的信息是宝藏。
感谢您的反馈和好评,Nav!
感谢您如此精彩的解释。我以前不知道 DGB。
感谢您的反馈,Ivo!