如何在pdb中观察变量

79 投票
6 回答
47605 浏览
提问于 2025-04-17 03:45

我正在调试一个Python脚本,想要监视一个变量的变化(就像在gdb中可以监视内存地址一样)。有没有办法做到这一点?

6 个回答

25

这里有一种很简单的方法可以用来实现这个功能,使用的是 pdb。你可以把这些命令放到你的 ~/.pdbrc 文件里,这样每次使用 pdb 时就会自动加载。

!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from pdb import Pdb as __Pdb
!global __pdb; __pdb = [__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self") for __framerec in __stack() if (__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self")).__class__ == __Pdb][-1]

alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if __currentframe().f_locals.has_key(__key) else __currentframe().f_globals; __val = __copy(%1)

alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")

alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])

这个方法添加了两个命令,分别是 nextwatchstepwatch,它们都需要一个变量名 varname 作为参数。如果可能的话,这两个命令会对当前的局部变量 varname 进行一个浅拷贝,然后分别继续执行 nextstep,直到这个变量指向的内容发生变化。

这个方法在 CPython 2.7.2 中有效,但它依赖于一些 pdb 的内部实现,所以在其他地方可能会出现问题。

33

对于Python 3

你可以使用display这个功能来帮助调试。

当你到达断点时,只需要输入:

ipdb> display 表达式

举个例子:

ipdb> display instance
display instance: <AppUser: dmitry4>
ipdb> display instance.id
display instance.id: 9
ipdb> display instance.university
display instance.university: <University: @domain.com>

ipdb> display

Currently displaying:
instance.university: <University: @domain.com>
instance.id: 9
instance: <AppUser: dmitry4>
ipdb> 

如你所见,每次输入display时,它都会打印出你设置的所有监视变量(表达式)。你可以使用内置的undisplay函数来移除某个监视变量。

你还可以使用pp 表达式来美化输出的表达式(这个功能非常有用)。

45

使用PDB进行数据断点

...就像你可以在gdb中监视一个内存地址一样...

  • GDB支持数据断点,这得益于硬件的支持(硬件监视点)。这通常涉及将内存页面标记为只读,这样在访问内存时就会触发异常处理程序。当没有硬件监视点时,它会使用软件监视点,这种方法只适用于单线程,并且速度较慢。
  • PDB不支持数据断点,所以简单来说,不可以,PDB无法直接做到这一点

在PDB中遇到断点时打印变量

当你在遇到断点时想要查看一个变量,可以使用commands命令。例如,在遇到断点#1时打印some_variable这是来自pdb文档的经典示例)。

(Pdb) commands 1
(com) print(some_variable)
(com) end
(Pdb)

另外,你可以使用condition命令,确保只有在变量取某个特定值时才会触发断点。

例如:

(Pdb) condition 1 some_variable==some_value

其他解决方案

你可以使用跟踪/分析功能,通过sys.settrace逐步检查代码,并查看正在执行的操作码。

这里有一些代码可以帮助你入门:

import sys
import dis


def tracefn(frame, event, arg):
    if event == 'call':
        print("## CALL", frame)
        frame.f_trace_opcodes = True
    elif event == 'opcode':
        opcode = frame.f_code.co_code[frame.f_lasti]
        opname = dis.opname[opcode]
        print("## OPCODE", opname)
    return tracefn


watchme = 123

def foo():
    global watchme
    watchme = 122

sys.settrace(tracefn)

foo()

你可能需要监视所有的STORE_*操作码。https://docs.python.org/3/library/dis.html

撰写回答