为啥要导入 pdb; pdb.set_trace 在 Spyder 中以不同方式调用时会触发两种不同的调试场景?

Posted

技术标签:

【中文标题】为啥要导入 pdb; pdb.set_trace 在 Spyder 中以不同方式调用时会触发两种不同的调试场景?【英文标题】:Why does import pdb; pdb.set_trace trigger two different debugging scenarios when called differently in Spyder?为什么要导入 pdb; pdb.set_trace 在 Spyder 中以不同方式调用时会触发两种不同的调试场景? 【发布时间】:2018-06-23 23:59:29 【问题描述】:

这是Stepwise debugging of selected Python code 的后续问题。

为什么import pdb; pdb.set_trace 在 Spyder 中以不同方式调用时会触发两种不同的调试场景?

这是answer from Carlos Cordoba中针对上述问题编辑的示例代码。

代码:

def foo():
    names = ['A', 'B', 'C']
    values = [11,12,13]

    i = 0
    import pdb; pdb.set_trace()
    for n in names:
        variable = str(n)  + ' = ' + str(values[i])
        print(variable)
        i += 1      
foo()

场景 1 - Run file (F5)Continue Execution until next breakpoint (F12)Run Current Line (F10)

此程序运行良好。为了上下文,让我解释一下:

Run file (F5) 突出显示第 2 行:

继续使用Continue Execution until next breakpoint (F12) 会直接转到第 8 行。另外请注意,在下面的屏幕截图中,变量资源管理器中填充了变量 inamesvalues。当您使用Run Current Line (F10) 浏览其余代码时,会添加和更新其他变量:

或者您可以Continue Execution until next breakpoint (F12) 并以这种方式完成程序。将前者一直执行到foo() 会清除变量资源管理器,在 ipdb 调试器中打印 --Return-- 并退出调试器。

我想我每次都应该采用这种方式做事,但我对 Spyder 提供的其他运行代码选项非常感兴趣。而且我特别喜欢用#%% 和 Ctrl+Enter 定义和运行单元格。

场景 2 - Ctrl+Enter 在包含整个代码的单元格中

单元格中的 Ctrl+Enter 突出显示第 8 行,并填充变量资源管理器:

继续Continue Execution until next breakpoint (F12) 清除变量资源管理器并像以前一样退出调试器:

这也很好,但这是我的情况:

场景 3 - 运行和调试多个单元

当我调试大型数据科学项目的代码片段时,我经常会在一个地方定义一些变量,并希望在其他地方调试使用这些变量作为输入的函数。这就是为什么我经常遇到以下情况的原因,我在一个单元格中定义了变量,并在另一个单元格中使用相同的变量进行了 For 循环:

包含变量的单元格

包含 For 循环的单元格

但是按 Ctrl+Enter 并继续运行当前行 (F10) 会在 interactiveshell.py 中触发混乱情况:

现在,问题:

    这是怎么回事? 可以避免这种情况吗? 为什么不能像这样调试单元格(或突出显示的代码 + f9)?

感谢您的任何建议!

【问题讨论】:

【参考方案1】:

此处是 Spyder 维护者)我认为问题在于您正在尝试评估函数范围内的单元格。在这种情况下,所有变量都是作用域的本地变量,因此您无法对 foo 进行分段计算,而这正是您尝试对我们的单元格执行的操作。

要实现你想要的,你可以使用一个类来代替。这将允许您将数据保存在类中所有方法共享的变量中,并定义您想要操作这些数据的函数/方法。一个类还允许您将数据和函数/方法巧妙地封装起来,即无需在代码的全局范围内定义。

这样,您可以简单地运行一个调用您要调试的方法的单元。在您上面发布的示例中,这将是

# %%
class Foo:
    def __init__(self):
        self.names = ['A', 'B', 'C']
        self.values = [11,12,13]

    def manipulation(self):
        i = 0
        import pdb; pdb.set_trace()
        for n in self.names:
            variable = str(n)  + ' = ' + str(self.values[i])
            print(variable)
            i += 1      

f = Foo()

# %%
f.manipulation()

这让我可以毫无问题地调试manipulation 方法的工作原理。

【讨论】:

以上是关于为啥要导入 pdb; pdb.set_trace 在 Spyder 中以不同方式调用时会触发两种不同的调试场景?的主要内容,如果未能解决你的问题,请参考以下文章

PDB 常用命令

用pdb.set_trace()设断点,跟nova/api/openstack/compute/servers.py - detail() 流程

PDB调试方法

使用pdb动态调试python代码

通过 pdb 调试 djcelery 的 celeryd

pdb 调试初步