python 调试器中的所有变量都未定义

all variables are undefined in python debugger

我在 Python 3.6 上遇到了一个非常奇怪的问题。在我的代码中间,我调用 import pdb; pdb.set_trace() 来调试一些代码。

然后我无法正确调试,例如:

(Pdb) abc = 3
(Pdb) [abc for _ in range(2)]
*** NameError: name 'abc' is not defined
(Pdb) [abc, abc]
[3, 3]

似乎每当我使用列表理解时,都会出现未定义变量的问题。但是,如果我在打开 Python 后立即调用调试器,我没有观察到这种行为,一切运行正常。

知道我为什么会遇到这个问题吗?

发生这种情况是因为列表理解(大部分)在嵌套范围内进行评估,而在 pdb 中创建的嵌套范围无法访问正在检查的范围的局部变量。但是,它们可以访问全局变量,并且当您在打开 Python 后立即启动 pdb 时,您 运行 它处于全局范围内,因此您创建的 abc 是全局的。

exec 和 class 语句中的列表理解也会发生这种情况。不幸的是,没有比 "don't use list comprehensions there".

更好的解决方法了

归档为 b.p.o. issue #21161,但关闭为 "won't fix"。

根本问题正是 所说的:Comprehensions1 通过定义然后 运行 一个隐藏的嵌套函数来工作,但是在 pdb 中定义的函数框架内不是真正的嵌套函数,不能从框架范围访问变量。

有一些解决方法(b.p.o 问题中提到了其中一些,或者从该问题链接的地方),但它们都只是其中之一的巧妙变体:

  • (lambda abc: [abc for _ in range(2)])(abc)(换句话说,定义一个函数并明确地将局部值作为参数传递而不是捕获它们。)
  • [abc_ for abc_ in [abc] for _ in range(2)](换句话说,使用最外层的可迭代对象是参数的事实。2
  • [loc['abc'] for loc in [locals()] for _ in range(2)](换句话说,使用locals()['name']而不是name,并像上面那样获取locals。)
  • 使用 exec 加上任何已知的同样可怕的解决方法来理解 exec
  • 不要像 user2357112 所建议的那样使用推导式。

1。在 Python 2.x 中,列表推导式并非如此,仅集和字典推导式以及生成器表达式。但是 Python 3 更改了列表推导式以使其与其他推导式保持一致。

2。理解的隐藏嵌套函数从封闭范围捕获 几乎 所有名称——但最外层的可迭代对象(第一个 for 中的那个)是特殊的;它实际上是隐藏函数的参数,表达式在封闭范围内计算并作为参数值传递。

您可以试试下面的代码:

(Pdb) !import code; code.interact(local=vars())
Python 3.6.6 |Anaconda, Inc.| (default, Jun 28 2018, 17:14:51) 
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> [abc for _ in range(2)]

使用 Ctrl-D return 进入常规 pdb 提示符。

类似于 Panfeng Li 的回答,但更简单的方法是在 pdb 中使用 interact

以下是 pdb 失败的示例,并且 interact 在同一上下文中工作。

Python 3.6.3 (default, Oct  4 2017, 06:09:05)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def do_comp():
...   abc = 3
...   import pdb; pdb.set_trace()
...
>>> do_comp()
--Return--
> <stdin>(3)do_comp()->None
(Pdb) [abc for _ in range(2)]
*** NameError: name 'abc' is not defined
(Pdb) interact
*interactive*
>>> [abc for _ in range(2)]
[3, 3]