Bil*_*ill 5 python ipython nameerror pdb
我无法理解此Python调试程序会话中引发的以下异常:
(Pdb) p [move for move in move_values if move[0] == max_value]
*** NameError: name 'max_value' is not defined
(Pdb) [move for move in move_values]
[(0.5, (0, 0)), (0.5, (0, 1)), (0.5, (0, 2)), (0.5, (1, 0)), (0.5, (1, 1)), (0.5, (1, 2)), (0.5, (2, 0)), (0.5, (2, 1)), (0.5, (2, 2))]
(Pdb) max_value
0.5
(Pdb) (0.5, (0, 2))[0] == max_value
True
(Pdb) [move for move in move_values if move[0] == 0.5]
[(0.5, (0, 0)), (0.5, (0, 1)), (0.5, (0, 2)), (0.5, (1, 0)), (0.5, (1, 1)), (0.5, (1, 2)), (0.5, (2, 0)), (0.5, (2, 1)), (0.5, (2, 2))]
(Pdb) [move for move in move_values if move[0] == max_value]
*** NameError: name 'max_value' is not defined
Run Code Online (Sandbox Code Playgroud)
为什么有时会告诉我max_value未定义,而有时却告诉我?
顺便说一下,这是调试器启动之前的代码:
max_value = max(move_values)[0]
best_moves = [move for move in move_values if move[0] == max_value]
import pdb; pdb.set_trace()
Run Code Online (Sandbox Code Playgroud)
我正在使用在PyCharm中运行的Python 3.6。
修改后的更新:
经过更多测试后,pdb当我从iPython REPL或PyCharm中执行以下操作时,局部变量似乎在会话的列表理解范围内不可见:
$ ipython
Python 3.6.5 | packaged by conda-forge | (default, Apr 6 2018, 13:44:09)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import pdb; pdb.set_trace()
--Call--
> /Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/IPython/core/displayhook.py(247)__call__()
-> def __call__(self, result=None):
(Pdb) x = 1; [x for i in range(3)]
*** NameError: name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)
但是在常规的Python REPL中,它可以工作:
$ python
Python 3.6.5 | packaged by conda-forge | (default, Apr 6 2018, 13:44:09)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb; pdb.set_trace()
--Return--
> <stdin>(1)<module>()->None
(Pdb) x = 1; [x for i in range(3)]
[1, 1, 1]
Run Code Online (Sandbox Code Playgroud)
我在上面使用版本3.4、3.5、3.6进行了测试,因此它似乎并不依赖于版本。
更新2
请注意,上述测试(“修正的更新”)存在问题,因为它import pdb; pdb.set_trace()在交互式REPL中使用。
另外,原始问题不仅限于iPython。
请参阅下面的user2357112的答案,以了解此处的最新情况。
对不起,如果造成任何混乱!
use*_*ica 12
这里有两个核心问题。第一个是(pdb.set_trace()在 IPython 中以交互方式调用时)您正在调试 IPython 的内脏而不是您想要的范围。第二个是列表推导式作用域规则与无法静态确定封闭作用域中存在的变量(例如在调试器或类主体中)的情况下相互作用很差。
第一个问题几乎只在输入pdb.set_trace()IPython 交互式提示时发生,这不是一件非常有用的事情,因此避免该问题的最简单方法就是不这样做。如果你无论如何都想这样做,你可以输入r几次命令,直到 pdb 说你没有 IPython 的胆量。(不要过度,否则你最终会进入 IPython 胆量的不同部分。)
第二个问题是根深蒂固的语言设计决策之间本质上不可避免的交互。不幸的是,它不太可能消失。调试器中的列表推导式仅适用于全局范围,而不适用于调试函数。如果您想在调试函数时构建一个列表,最简单的方法可能是使用interact命令并编写一个for循环。
这是进入此效果的完整组合。
pdb.set_trace()在下一个跟踪事件上触发 pdb ,而不是在pdb.set_trace()调用时触发。pdb 和其他 Python 调试器使用的跟踪功能机制仅在某些特定事件上触发,不幸的是,“设置跟踪功能时”不是这些事件之一。通常,下一个事件要么是'line'下一行的'return'事件,要么是当前代码对象执行结束的事件,但这不是这里发生的情况。
Python 用来显示表达式语句结果的机制是sys.displayhook. 当您1+2在交互式提示下执行以下操作时:
>>> 1+2
3
Run Code Online (Sandbox Code Playgroud)
sys.displayhook是打印3而不是丢弃它。它也设置_. 当表达式语句的结果为 时None,例如使用 expression 时pdb.set_trace(),sys.displayhook什么都不做,但它仍然被调用。
IPython 替换sys.displayhook为它自己的自定义处理程序,负责打印Out[n]:事物、设置记录中的条目Out、调用 IPython 自定义漂亮打印以及各种其他 IPython 便利。对于我们的目的,重要的是 IPython 的 displayhook 是用 Python 编写的,因此下一个跟踪事件是'call'displayhook的事件。
pdb在 IPython 的 displayhook 内开始调试。
In [1]: import pdb; pdb.set_trace()
--Call--
> /Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/IPython/core/displayhook.py(247)__call__()
-> def __call__(self, result=None):
Run Code Online (Sandbox Code Playgroud)
人们不喜欢列表推导式如何将循环变量泄漏到 Python 2 中的包含作用域中,因此列表推导式在 Python 3 中获得了它们自己的作用域。
eval,它与闭包变量的交互非常差。Python 的闭包变量机制依赖于静态作用域分析,这与eval工作原理完全不兼容。因此,内部创建的新作用域eval无法访问闭包变量;他们只能访问全局变量。
综上所述,在 IPython 中,您最终调试的是 IPython 显示钩子,而不是运行交互式代码的作用域。由于您在 IPython 的显示钩子中,因此您的x = 1分配进入了显示钩子的局部变量。随后的列表推导需要访问显示钩子的局部x变量来访问,但这将是列表推导的闭包变量,它不适用于eval.
在IPython之外,sys.displayhook是用C写的,所以pdb进不去,也没有'call'事件。您最终调试了您打算调试的范围。由于您处于全局范围内,因此x = 1进入全局范围,并且列表理解可以访问它。
如果您x = 1; [x for i in range(3)]在调试任何普通函数时尝试运行,您会看到相同的效果。
一个可能的解决方案/解决方法是运行
globals().update(locals())
Run Code Online (Sandbox Code Playgroud)
在 (i)pdb 中运行列表理解之前。