如何使用 Python 调试器

2023-11-10

介绍

在软件开发中,调试是查找并解决阻止软件正常运行的问题的过程。

Python调试器为Python程序提供了调试环境。它支持设置条件断点、一次一行单步执行源代码、堆栈检查等。

先决条件

您应该在计算机或服务器上安装 Python 3 并设置编程环境。如果您还没有设置编程环境,可以参考安装和设置指南本地编程环境或对于一个服务器上的编程环境适合您的操作系统(Ubuntu、CentOS、Debian 等)

与 Python 调试器交互工作

Python 调试器作为标准 Python 发行版的一部分作为一个名为pdb。调试器也是可扩展的,并被定义为类Pdb。您可以阅读的官方文档pdb了解更多。

Info:要按照本教程中的示例代码进行操作,请通过运行以下命令在本地系统上打开 Python 交互式 shellpython3命令。然后,您可以复制、粘贴或编辑示例,方法是将它们添加到>>> prompt.

我们将从一个简短的程序开始,该程序有两个全局变量变量, a function创建一个嵌套的loop,以及if __name__ == '__main__':建设将调用nested_loop()功能。

循环.py
num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']


def nested_loop():
    for number in num_list:
        print(number)
        for letter in alpha_list:
            print(letter)

if __name__ == '__main__':
    nested_loop()

我们现在可以使用以下命令通过 Python 调试器运行该程序:

  1. python -mpdb循环.py

The -m命令行标志将为您导入任何Python模块并将其作为脚本运行。在本例中,我们导入并运行pdb模块,我们将其传递到命令中,如上所示。

运行此命令后,您将收到以下输出:

Output
> /Users/sammy/looping.py(1)<module>() -> num_list = [500, 600, 700] (Pdb)

In the output, the first line contains the current module name (as indicated with <module>) with a directory path, and the printed line number that follows (in this case it’s 1, but if there is a comment or other non-executable line it could be a higher number). The second line shows the current line of source code that is executed here, as pdb provides an interactive console for debugging. You can use the command help to learn its commands, and help command to learn more about a specific command. Note that the pdb console is different than the Python interactive shell.

当 Python 调试器到达程序末尾时,它会自动重新启动。每当你想离开的时候pdb控制台,输入命令quit or exit。如果您想在程序中的任何位置显式重新启动程序,可以使用以下命令来执行此操作run.

使用调试器移动程序

在 Python 调试器中处理程序时,您可能会使用list, step, and next用于移动代码的命令。我们将在本节中回顾这些命令。

在 shell 中,我们可以输入命令list为了获得当前行周围的上下文。从程序的第一行开始looping.py我们上面展示的——num_list = [500, 600, 700]- 看起来像下面这样:

(Pdb) list
  1  ->	num_list = [500, 600, 700]
  2  	alpha_list = ['x', 'y', 'z']
  3  	
  4  	
  5  	def nested_loop():
  6  	    for number in num_list:
  7  	        print(number)
  8  	        for letter in alpha_list:
  9  	            print(letter)
 10  	
 11  	if __name__ == '__main__':
(Pdb) 

当前行用字符指示->,在我们的例子中是程序文件的第一行。

由于这是一个相对较短的程序,因此我们几乎收到了所有程序list命令。在不提供论据的情况下,list命令在当前行周围提供 11 行,但您也可以指定要包含哪些行,如下所示:

(Pdb) list 3, 7
  3  	
  4  	
  5  	def nested_loop():
  6  	    for number in num_list:
  7  	        print(number)
(Pdb) 

这里,我们要求使用命令显示第3-7行list 3, 7.

要逐行浏览程序,我们可以使用step or next:

(Pdb) step
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb) 
(Pdb) next
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb) 

和...之间的不同step and next就是它step将在被调用函数内停止,而next执行被调用的函数仅在当前函数的下一行停止。当我们使用该函数时,我们可以看到这种差异。

The step一旦进入函数运行状态,命令将迭代循环,准确显示循环正在执行的操作,因为它首先会打印一个数字print(number)然后通过打印字母print(letter),返回数字等:

(Pdb) step
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb) step
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) step
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb) step
--Call--
> /Users/sammy/looping.py(5)nested_loop()
-> def nested_loop():
(Pdb) step
> /Users/sammy/looping.py(6)nested_loop()
-> for number in num_list:
(Pdb) step
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb) step
500
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb) step
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb) step
x
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb) step
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb) step
y
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)

The next相反,命令将执行整个函数,而不显示分步过程。让我们退出当前会话exit命令,然后再次启动调试器:

  1. python -mpdb循环.py

现在我们可以与next命令:

(Pdb) next
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb) next
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) next
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb) next
500
x
y
z
600
x
y
z
700
x
y
z
--Return--
> /Users/sammy/looping.py(12)<module>()->None
-> nested_loop()
(Pdb)  

在检查代码时,您可能想要检查传递给变量的值,您可以使用pp命令,它将使用以下命令漂亮地打印表达式的值pprint module:

(Pdb) pp num_list
[500, 600, 700]
(Pdb) 

大多数命令在pdb有更短的别名。为了step那个简短的形式是s,并且对于next it is n. The help命令将列出可用的别名。您还可以通过按ENTER按提示键。

断点

您通常会使用比上面的示例更大的程序,因此您可能想要查看特定的函数或行,而不是浏览整个程序。通过使用break命令设置断点,您将运行程序直到指定的断点。

当您插入断点时,调试器会为其分配一个编号。分配给断点的数字是以数字 1 开头的连续整数,您在使用断点时可以参考该数字。

可以按照以下语法将断点放置在某些行号处<program_file>:<line_number>如下图所示:

(Pdb) break looping.py:5
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)

Type clear进而y删除所有当前断点。然后,您可以在定义函数的位置放置一个断点:

(Pdb) break looping.nested_loop
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb) 

要删除当前断点,请键入clear进而y。您还可以设置一个条件:

(Pdb) break looping.py:7, number > 500
Breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)     

现在,如果我们发出continue命令,程序将中断number x被评估为大于 500(即,当在外循环的第二次迭代中将其设置为等于 600 时):

(Pdb) continue
500
x
y
z
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb) 

要查看当前设置为运行的断点列表,请使用以下命令break没有任何争论。您将收到有关您设置的断点的特殊性的信息:

(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /Users/sammy/looping.py:7
	stop only if number > 500
	breakpoint already hit 2 times
(Pdb) 

我们还可以使用命令禁用断点disable以及断点的数量。在此会话中,我们添加另一个断点,然后禁用第一个断点:

(Pdb) break looping.py:11
Breakpoint 2 at /Users/sammy/looping.py:11
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep no    at /Users/sammy/looping.py:7
	stop only if number > 500
	breakpoint already hit 2 times
2   breakpoint   keep yes   at /Users/sammy/looping.py:11
(Pdb) 

要启用断点,请使用enable命令,要完全删除断点,请使用clear命令:

(Pdb) enable 1
Enabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) clear 2
Deleted breakpoint 2 at /Users/sammy/looping.py:11
(Pdb) 

断点位于pdb为您提供很多控制权。一些附加功能包括在程序当前迭代期间忽略断点ignore命令(如ignore 1),在断点处触发动作发生commands命令(如command 1),并创建临时断点,当程序第一次执行到该命令的点时,这些断点会自动清除tbreak(例如,对于第 3 行的临时中断,您可以输入tbreak 3).

整合pdb进入程序

您可以通过导入来触发调试会话pdb模块并添加pdb功能pdb.set_trace()位于您希望会话开始的行上方。

在上面的示例程序中,我们将添加import语句和我们要进入调试器的函数。对于我们的示例,让我们将其添加到嵌套循环之前。

# Import pdb module
import pdb

num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']


def nested_loop():
    for number in num_list:
        print(number)

        # Trigger debugger at this line
        pdb.set_trace()
        for letter in alpha_list:
            print(letter)

if __name__ == '__main__':
    nested_loop()

通过将调试器添加到代码中,您不需要以特殊方式启动程序或记住设置断点。

导入pdb模块并运行pdb.set_trace()函数允许您像往常一样开始程序并在其执行过程中运行调试器。

修改程序执行流程

Python 调试器允许您在运行时更改程序的流程jump命令。这可以让您向前跳以阻止某些代码运行,或者可以让您向后退以再次运行代码。

我们将使用一个小程序来创建字符串中包含的字母列表sammy = "sammy":

letter_list.py
def print_sammy():
    sammy_list = []
    sammy = "sammy"
    for letter in sammy:
        sammy_list.append(letter)
        print(sammy_list)

if __name__ == "__main__":
    print_sammy()

如果我们像往常一样运行程序python letter_list.py命令,我们将收到以下输出:

Output
['s'] ['s', 'a'] ['s', 'a', 'm'] ['s', 'a', 'm', 'm'] ['s', 'a', 'm', 'm', 'y']

使用 Python 调试器,我们首先展示如何更改执行向前跳跃第一个周期后。当我们这样做时,我们会注意到存在中断for loop:

  1. python -mpdb letter_list.py
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb) list
  1  ->	def print_sammy():
  2  	    sammy_list = []
  3  	    sammy = "sammy"
  4  	    for letter in sammy:
  5  	        sammy_list.append(letter)
  6  	        print(sammy_list)
  7  	
  8  	if __name__ == "__main__":
  9  	    print_sammy()
 10  	
 11  	
(Pdb) break 5
Breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb) continue
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) pp letter
's'
(Pdb) continue
['s']
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) jump 6
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
'a'
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb) continue
['s']
['s', 'm']
['s', 'm', 'm']
['s', 'm', 'm', 'y']

上面的调试会话在第 5 行放置一个中断以防止代码继续,然后继续执行代码(同时漂亮地打印一些值)letter显示正在发生的事情)。接下来,我们使用jump命令跳到第 6 行。此时,变量letter设置为等于字符串'a',但是我们跳转将其添加到列表中的代码sammy_list。然后我们禁用断点以像往常一样继续执行continue命令,所以'a'永远不会附加到sammy_list.

接下来,我们可以退出第一个会话并重新启动调试器跳回来在程序中重新运行已经执行过的语句。这次,我们将运行第一次迭代for在调试器中再次循环:

> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb) list
  1  ->	def print_sammy():
  2  	    sammy_list = []
  3  	    sammy = "sammy"
  4  	    for letter in sammy:
  5  	        sammy_list.append(letter)
  6  	        print(sammy_list)
  7  	
  8  	if __name__ == "__main__":
  9  	    print_sammy()
 10  	
 11  	
(Pdb) break 6
Breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb) continue
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
's'
(Pdb) jump 5
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) continue
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
's'
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb) continue
['s', 's']
['s', 's', 'a']
['s', 's', 'a', 'm']
['s', 's', 'a', 'm', 'm']
['s', 's', 'a', 'm', 'm', 'y']

在上面的调试会话中,我们在第 6 行添加了一个中断,然后在继续后跳回第 5 行。我们一路漂亮地打印来表明字符串's'正在被追加到列表中sammy_list两次。然后我们禁用了第 6 行的中断并继续运行程序。输出显示两个值's'附加到sammy_list.

调试器会阻止某些跳转,尤其是在跳入和跳出某些未定义的流控制语句时。例如,在定义参数之前不能跳转到函数,也不能跳转到 a 的中间try:except陈述。你也无法跳出finally block.

The jump使用 Python 调试器的语句允许您在调试程序时更改执行流程,以查看是否可以修改流程控制以适应不同的目的,或者更好地了解代码中出现的问题。

常用表pdb命令

这是一个有用的表格pdb使用 Python 调试器时要记住的命令及其简短形式。

Command Short form What it does
args a Print the argument list of the current function
break b Creates a breakpoint (requires parameters) in the program execution
continue c or cont Continues program execution
help h Provides list of commands or help for a specified command
jump j Set the next line to be executed
list l Print the source code around the current line
next n Continue execution until the next line in the current function is reached or returns
step s Execute the current line, stopping at first possible occasion
pp pp Pretty-prints the value of the expression
quit or exit q Aborts the program
return r Continue execution until the current function returns

您可以从以下位置阅读有关命令和使用调试器的更多信息Python 调试器文档.

结论

调试是任何软件开发项目的重要步骤。 Python 调试器pdb实现一个交互式调试环境,您可以将其与任何用 Python 编写的程序一起使用。

借助可让您暂停程序、查看变量设置的值以及以离散的逐步方式执行程序的功能,您可以更全面地了解程序正在执行的操作并查找存在的错误逻辑或解决已知问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Python 调试器 的相关文章

随机推荐

  • 如何在 CentOS 8 上安装 Gitea

    Gitea 是一个用 Go 编写的自托管开源 git 服务器 它配备了存储库文件编辑器 项目问题跟踪 用户管理 通知 内置 wiki 等等 Gitea是一个轻量级应用程序 可以安装在功能较弱的系统上 如果您正在寻找内存占用小得多的 Gitl
  • 如何在Ubuntu 20.04服务器上安装Python 3并设置编程环境

    介绍 对于初学者和经验丰富的开发人员来说 Python 编程语言越来越受欢迎 Python 灵活且用途广泛 在脚本编写 自动化 数据分析 机器学习和后端开发方面具有优势 首次发布于 1991 年 其名称的灵感来自英国喜剧团体 Monty P
  • 如何在 Ubuntu 12.04 上添加交换

    Status 已弃用 本文介绍不再受支持的 Ubuntu 版本 如果您当前运行的服务器运行 Ubuntu 12 04 我们强烈建议您升级或迁移到受支持的 Ubuntu 版本 升级到Ubuntu 14 04 从 Ubuntu 14 04 升级
  • 使用 Kotlin 的 Android 进度条

    在本教程中 我们将使用 Kotlin 在 Android 应用程序中讨论和实现 ProgressBar 什么是进度条 ProgressBar UI 元素用于在应用程序屏幕上显示进度 我们可以使用 ProgressBar 在应用程序屏幕上显示
  • 如何在 Ubuntu 18.04 上设置 OpenVPN 服务器

    本教程的先前版本由以下人员编写贾斯汀 埃林伍德 介绍 当您的智能手机或笔记本电脑连接到不受信任的网络 例如酒店或咖啡店的 WiFi 时 想要安全可靠地访问互联网吗 A虚拟专用网络 VPN 允许您安全地穿越不受信任的网络 就像在专用网络上一样
  • 保护服务器的推荐安全措施

    介绍 大多数时候 您的主要关注点是启动并运行云应用程序 作为设置和部署过程的一部分 在公开可用之前为您的系统和应用程序构建强大而彻底的安全措施非常重要 在部署应用程序之前实施本教程中的安全措施将确保您在基础架构上运行的任何软件都具有安全的基
  • Android TabLayout 和 ViewPager

    在本教程中 我们将在已经实现的 TabLayout 下实现 ViewPagerthis教程 Android TabLayout ViewPager 概述 ViewPagers 用于滑动数据页 它通常与片段结合使用 让我们修改之前教程中的布局
  • 如何在 Apache 上为 Ubuntu 12.04 创建 SSL 证书

    Status 已弃用 本文介绍不再受支持的 Ubuntu 版本 如果您当前运行的服务器运行 Ubuntu 12 04 我们强烈建议您升级或迁移到受支持的 Ubuntu 版本 升级到Ubuntu 14 04 从 Ubuntu 14 04 升级
  • SQL面试题及答案

    几乎所有面试都会问到SQL面试问题 因为数据库操作在应用中非常常见 SQL 代表结构化查询语言 它是一种用于数据库通信和关系数据库管理的特定领域编程语言 SQL 由用于数据库交互的标准命令组成 例如 SELECT INSERT CREATE
  • 了解 React useMemo Hook

    介绍 随着 React 16 8 的发布 您现在可以在 React 应用程序中使用许多有用的钩子 16 8 中引入的内置 Hooks 之一是useMemo 该挂钩有可能提高应用程序的性能 本文将探讨重新渲染在 React 中的工作原理 为什
  • 了解 SSH 加密和连接过程

    介绍 SSH或安全 shell 是一种安全协议 也是安全管理远程服务器的最常用方法 SSH 使用多种加密技术 提供了一种在两方之间建立加密安全连接 向另一方验证每一方以及来回传递命令和输出的机制 在本指南中 我们将研究 SSH 使用的底层加
  • 如何在 Ubuntu 14.04 上导入和导出 MongoDB 数据库

    MongoDB 是最流行的 NoSQL 数据库引擎之一 它以可扩展 功能强大 可靠且易于使用而闻名 在本文中 我们将向您展示如何导入和导出 MongoDB 数据库 我们应该明确指出 本文中的导入和导出是指以人类可读的格式处理数据 并与其他软
  • 如何使用 Java List 和 ListArray 的 remove() 方法

    介绍 Java列表remove 方法用于从列表中删除元素 ArrayList是最广泛使用的实现列表界面 所以这里的例子将使用ArrayList remove 方法 Java列表remove Methods 那里有两个remove 方法从列表
  • 在 SSH Unix 服务器上运行 Shell 命令的 Java JSch 示例

    今天我们将研究 JSch 示例教程 我们可以使用 JSch 在 java 中创建 SSH 连接 早些时候我写了一个程序来连接SSH服务器上的远程数据库 今天 我将介绍一个可用于连接到启用 SSH 的服务器并执行 shell 命令的程序 我在
  • R 中的 Paste() 函数 - 简要指南

    在R 中使用paste 函数将是直接且简单的 在本教程中 让我们看看如何使用paste 来连接字符串和值 paste 从多个向量中获取多个元素并将它们连接成一个元素 除了paste 函数之外 R还有另一个名为paste0 的函数 是的 你没
  • DigitalOcean 的技术写作指南

    DigitalOcean 很高兴能够继续构建与服务器管理和软件工程相关的技术文章集 为了确保 DigitalOcean 文章具有一致的质量和风格 我们制定了以下准则 本指南分为四个部分 Style 我们编写技术教程的高级方法 结构 对我们的
  • 如何使用 Vanilla JavaScript 和 HTML 创建拖放元素

    介绍 拖放是一种常见的用户交互 您可以在许多图形用户界面中找到它 有预先存在的 JavaScript 库可用于向您的应用程序添加拖放功能 但是 在某些情况下 库可能不可用 或者会引入项目不需要的开销或依赖项 在这些情况下 了解现代 Web
  • 如何在 Ubuntu 18.04 上安装 Apache Tomcat 9

    介绍 Apache Tomcat 是一个 Web 服务器和 servlet 容器 用于为 Java 应用程序提供服务 Tomcat 是 Java Servlet 和 JavaServer Pages 技术的开源实现 由 Apache Sof
  • 如何在 Apache Web 服务器中安装、配置和使用模块

    Status 已弃用 本文介绍不再受支持的 Ubuntu 版本 如果您当前运行的服务器运行 Ubuntu 12 04 我们强烈建议您升级或迁移到受支持的 Ubuntu 版本 升级到Ubuntu 14 04 从 Ubuntu 14 04 升级
  • 如何使用 Python 调试器

    介绍 在软件开发中 调试是查找并解决阻止软件正常运行的问题的过程 Python调试器为Python程序提供了调试环境 它支持设置条件断点 一次一行单步执行源代码 堆栈检查等 先决条件 您应该在计算机或服务器上安装 Python 3 并设置编