在Python中设置断点和异常钩子

Python 调试代码有多种方法,其中一种方法是在代码中您希望调用 Python 调试器的位置设置断点。用于在不同调用点进入调试会话的语句取决于您使用的 Python 解释器版本,在本教程中我们将看到这一点。 

在本教程中,您将发现设置不同 Python 版本断点的各种方法。 

完成本教程后,您将了解:

  • 如何在早期 Python 版本中调用 pdb 调试器
  • 如何在 Python 3.7 中使用新的内置 breakpoint() 函数
  • 如何编写自己的 breakpoint() 函数以简化早期 Python 版本的调试过程
  • 如何使用事后调试器

使用我新书《Python for Machine Learning快速启动您的项目,书中包含分步教程和所有示例的Python源代码文件。

让我们开始吧。

在不同 Python 版本中设置断点
Josh Withers 摄,保留部分权利。

教程概述

本教程分为三个部分;它们是:

  • 在 Python 代码中设置断点
    • 在早期 Python 版本中调用 pdb 调试器
    • 在 Python 3.7 中使用 breakpoint() 函数
  • 为早期 Python 版本编写自己的 breakpoint() 函数
  • breakpoint() 函数的限制

在 Python 代码中设置断点

我们之前已经看到,调试 Python 脚本的一种方法是在命令行中使用 Python 调试器运行它。 

为此,我们需要使用 -m pdb 命令,该命令在执行 Python 脚本之前加载 pdb 模块。在同一个命令行界面中,然后我们会跟上一个您选择的特定调试器命令,例如 n 前进到下一行,或者如果您打算进入一个函数,则使用 s 

随着代码长度的增加,此方法很快就会变得麻烦。解决此问题并更好地控制中断代码位置的一种方法是直接在代码中插入断点。 

在早期 Python 版本中调用 pdb 调试器

在 Python 3.7 之前的版本中调用 pdb 调试器,您需要 import pdb 并调用 pdb.set_trace() 到您希望进入交互式调试会话的代码点。 

如果我们以 实现通用注意力机制 的代码为例,我们可以如下中断代码

执行脚本后,pdb 调试器会在我们计算 scores 变量之前启动,我们可以继续发出任何您选择的调试器命令,例如 n 前进到下一行,或 c 继续执行

虽然有效,但这并不是在代码中插入断点的最优雅和最直观的方法。Python 3.7 实现了一种更直接的方法,我们将在接下来看到。

在 Python 3.7 中使用 breakpoint() 函数 

Python 3.7 包含一个内置的 breakpoint() 函数,该函数在调用点(或放置 breakpoint() 语句的代码位置)进入 Python 调试器。 

调用时,breakpoint() 函数的默认实现将调用 sys.breakpointhook(),后者又会调用 pdb.set_trace() 函数。这很方便,因为我们无需 import pdb 并显式调用 pdb.set_trace() 。 

让我们重新审视实现通用注意力机制的代码,并用 breakpoint() 语句引入断点

使用 breakpoint() 函数的一个优点是,通过调用 sys.breakpointhook() 的默认实现,会查询一个新的环境变量 PYTHONBREAKPOINT。这个环境变量可以取不同的值,基于这些值可以执行不同的操作。 

例如,将 PYTHONBREAKPOINT 的值设置为 0 会禁用所有断点。因此,您的代码可以包含任意数量的断点,但这些断点可以轻松地停止执行代码,而无需物理移除它们。例如,如果包含该代码的脚本名称是main.py,我们可以通过在命令行界面中这样调用它来禁用所有断点

否则,我们可以通过在代码本身中设置环境变量来实现相同的结果

PYTHONBREAKPOINT 的值会在每次调用 sys.breakpointhook() 时被查询。这意味着这个环境变量的值可以在代码执行期间更改,并且 breakpoint() 函数也会相应地做出响应。 

PYTHONBREAKPOINT 环境变量也可以设置为其他值,例如可调用对象的名称。假设,例如,我们想使用 pdb 以外的其他 Python 调试器,例如 ipdb(如果调试器尚未安装,请先运行 pip install ipdb)。在这种情况下,我们可以在命令行界面中调用 main.py 脚本,并在不更改代码本身的情况下挂载调试器

这样做,breakpoint() 函数将在下一个调用点进入 ipdb 调试器

该函数还可以接受 breakpoint(*args, **kwargs) 作为输入参数,然后将这些参数传递给 sys.breakpointhook()。这是因为任何可调用对象(例如第三方调试器模块)都可能接受可选参数,这些参数可以通过 breakpoint() 函数传递。 

想开始学习机器学习 Python 吗?

立即参加我为期7天的免费电子邮件速成课程(附示例代码)。

点击注册,同时获得该课程的免费PDF电子书版本。

编写您自己的 Python 早期版本中的 breakpoint() 函数

让我们回到 Python 3.7 及更早版本没有现成的 breakpoint() 函数的事实。我们可以自己编写。 

与 Python 3.7 及更高版本中实现 breakpoint() 函数的方式类似,我们可以实现一个检查环境变量值并在

  • 如果环境变量的值设置为 0,则跳过代码中的所有断点。
  • 如果环境变量是空字符串,则进入默认的 Python pdb 调试器。
  • 根据环境变量的值进入另一个调试器。 

我们可以将此函数包含在代码中并运行它(在这种情况下,使用 Python 2.7 解释器)。如果我们将来设置环境变量的值为空字符串,我们会发现 pdb 调试器会停在代码中我们放置 `breakpoint()` 函数的那一点。然后我们可以从那里开始在命令行中输入调试器命令。

Similarly, if we set the environment variable to

The `breakpoint()` function that we have implemented now enters the ipdb debugger and stops at the call site

Setting the environment variable to 0 simply skips all breakpoints, and the computed attention output is returned in the command line, as expected

This facilitates the process of breaking into the code for Python versions earlier than v3.7 because it now becomes a matter of setting the value of an environment variable rather than having to manually introduce (or remove) the `import pdb; pdb.set_trace()` statement at different call sites in the code.

breakpoint() 函数的限制

The `breakpoint()` function allows you to bring in the debugger at some point in the program. You need to find the exact position that you need the debugger to put the breakpoint into it. If you consider the following code

This will bring you the debugger when the function `func()` raised exceptions. It can be triggered by the function itself or deep inside some other functions that it calls. But the debugger will start at the line `print("exception!")` above, which may not be very useful.

The way that we can bring up the debugger at the point of exception is called the post-mortem debugger. It works by asking Python to register the debugger `pdb.pm()` as the exception handler when an uncaught exception is raised. When it is called, it will look for the last exception raised and start the debugger at that point. To use the post-mortem debugger, we just need to add the following code before the program is run

This is handy because nothing else needs to be changed in the program. For example, assume we want to evaluate the average of $1/x$ using the following program. It is quite easy to overlook some corner cases, but we can catch the issue when an exception is raised

When we run the above program, the program may terminate, or it may raise a division by zero exception, depending on whether the random number generator ever produces zero in the loop. In that case, we may see the following

where we found the exception is raised at which line, and we can check the value of the variables as we can usually do in `pdb`.

In fact, it is more convenient to print the traceback and the exception when the post-mortem debugger is launched

And the debugger session will be started as follows

进一步阅读

如果您想深入了解,本节提供了更多关于该主题的资源。

网站

总结

In this tutorial, you discovered various ways of setting breakpoints in different versions of Python.

具体来说,你学到了:

  • 如何调用早期 Python 版本的 pdb 调试器
  • 如何使用 Python 3.7 中引入的新内置 `breakpoint()` 函数
  • 如何编写自己的 `breakpoint()` 函数以简化早期 Python 版本的调试过程

你有什么问题吗?

在下面的评论中提出您的问题,我将尽力回答。

掌握机器学习 Python!

Python For Machine Learning

更自信地用 Python 编写代码

...从学习实用的 Python 技巧开始

在我的新电子书中探索如何实现
用于机器学习的 Python

它提供自学教程数百个可运行的代码,为您提供包括以下技能:
调试性能分析鸭子类型装饰器部署等等...

向您展示高级 Python 工具箱,用于
您的项目


查看内容

, ,

暂无评论。

Leave a Reply

Machine Learning Mastery 是 Guiding Tech Media 的一部分,Guiding Tech Media 是一家领先的数字媒体出版商,专注于帮助人们了解技术。访问我们的公司网站以了解更多关于我们的使命和团队的信息。