iPython 调试器引发 `NameError: name ... is not defined` [英] iPython debugger raises `NameError: name ... is not defined`

查看:78
本文介绍了iPython 调试器引发 `NameError: name ... is not defined`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解在此 Python 调试器会话中引发的以下异常:

(Pdb) p [move for move in move_values if move[0] == max_value]*** NameError: name 'max_value' 未定义(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) 最大值0.5(Pdb) (0.5, (0, 2))[0] == max_value真的(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' 未定义

为什么有时告诉我 max_value 未定义,有时却没有?

顺便说一下,这是调试器启动前的代码:

max_value = max(move_values)[0]best_moves = [move for move in move_values if move[0] == max_value]导入 pdb;pdb.set_trace()

我使用的是在 PyCharm 中运行的 Python 3.6.

修正更新:

经过更多测试后,当我从 iPython REPL 或 PyCharm 执行以下操作时,在 pdb 会话中的列表推导式中似乎看不到局部变量:

$ ipythonPython 3.6.5 |由 conda-forge 打包 |(默认,2018 年 4 月 6 日,13:44:09)输入版权"、信用"或许可"以获取更多信息IPython 6.4.0——增强的交互式 Python.类型 '?'求助.在[1]中:导入pdb;pdb.set_trace()- 称呼 ->/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: 名称 'x' 未定义

但在常规 Python REPL 中它有效:

$ pythonPython 3.6.5 |由 conda-forge 打包 |(默认,2018 年 4 月 6 日,13:44:09)[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin输入帮助"、版权"、信用"或许可"以获取更多信息.>>>导入 pdb;pdb.set_trace()- 返回 -><stdin>(1)<module>()->无(Pdb) x = 1;[x for i in range(3)][1, 1, 1]

我使用 3.4、3.5、3.6 版本进行了上述测试,因此它似乎与版本无关.

更新 2

请注意,上面的测试('AMENDED UPDATE')是有问题的,因为它使用了 import pdb;交互式 REPL 中的 pdb.set_trace().

另外,原来的问题不限于iPython.

请参阅下面的用户 2357112 的回答,以全面了解此处发生的情况.

对不起,如果我造成了任何混淆!

解决方案

这里有两个核心问题.第一个是(在 IPython 中以交互方式调用 pdb.set_trace() 时)您正在调试 IPython 的内脏而不是您想要的范围.第二个是列表推导式作用域规则与无法静态确定封闭作用域中存在的变量的情况相互作用很差,例如在调试器或 类主体.

第一个问题几乎只发生在将 pdb.set_trace() 输入到 IPython 交互式提示中时,这不是一件非常有用的事情,因此避免该问题的最简单方法是只是不这样做.如果你无论如何都想这样做,你可以输入几次 r 命令,直到 pdb 说你没有 IPython 的胆量.(不要过头,否则你最终会进入 IPython 的不同部分.)

第二个问题是根深蒂固的语言设计决策之间本质上不可避免的交互.不幸的是,它不太可能消失.调试器中的列表推导式仅适用于全局范围,而不适用于调试函数.如果您想在调试函数时构建一个列表,最简单的方法可能是使用 interact 命令并编写一个 for 循环.

<小时>

这是进入此效果的完整组合.

  1. pdb.set_trace() 在下一个 trace 事件 上触发 pdb,而不是在调用 pdb.set_trace() 时.

pdb 和其他 Python 使用的 trace function 机制调试器仅在某些特定事件上触发,不幸的是,设置跟踪功能时"不是这些事件之一.通常,下一个事件要么是下一行的 'line' 事件,要么是当前代码对象执行结束的 'return' 事件,但这不是发生在这里.

  1. IPython 设置了一个 displayhook 自定义表达式语句处理.

Python 用来显示表达式语句结果的机制是 sys.displayhook.当您在交互式提示下执行 1+2 时:

<预><代码>>>>1+23

sys.displayhook 是打印 3 而不是丢弃它.它还设置 _.当表达式语句的结果为 None 时,例如表达式 pdb.set_trace()sys.displayhook 什么也不做,但它是还是打电话.

IPython 用自己的自定义处理程序替换 sys.displayhook,负责打印 Out[n]: 东西,用于设置 Out record,用于调用 IPython 自定义漂亮打印,以及各种其他 IPython 便利.对于我们的目的,重要的是 IPython 的显示钩子是用 Python 编写的,所以下一个跟踪事件是显示钩子的 'call' 事件.

pdb 开始调试在 IPython 的显示钩子内.

在[1]中:导入pdb;pdb.set_trace()- 称呼 ->/Users/billtubbs/anaconda/envs/py36/lib/python3.6/site-packages/IPython/core/displayhook.py(247)__call__()->def __call__(self, result=None):

  1. 列表推导式创建了一个新的范围.

人们不喜欢列表推导式如何将循环变量泄漏到 Python 2 中的包含作用域中,因此列表推导式在 Python 3 中获得了它们自己的作用域.

  1. pdb 使用 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)] 调试任何普通函数时.

I can't understand the following exception that is raised in this Python debugger session:

(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

Why is it sometimes telling me max_value is not defined and other times not?

Incidentally, this is the code immediately prior to the debugger starting:

max_value = max(move_values)[0]
best_moves = [move for move in move_values if move[0] == max_value]
import pdb; pdb.set_trace()

I am using Python 3.6 running in PyCharm.

AMENDED UPDATE:

After more testing it appears that local variables are not visible within list comprehensions within a pdb session when I do the following from an iPython REPL or in 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

But in a regular Python REPL it works:

$ 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]

I tested above with versions 3.4, 3.5, 3.6 so it does not appear to be version dependent.

UPDATE 2

Please note, the above test ('AMENDED UPDATE') is problematic because it uses import pdb; pdb.set_trace() in the interactive REPL.

Also, the original problem is not limited to iPython.

See answer by user2357112 below for a comprehensive explanation of what is going on here.

Sorry if I caused any confusion!

解决方案

You've got two core problems here. The first is that (when calling pdb.set_trace() interactively in IPython) you're debugging IPython's guts instead of the scope you wanted. The second is that list comprehension scoping rules interact badly with cases where the variables present in enclosing scopes can't be determined statically, such as in debuggers or class bodies.

The first problem pretty much only happens when typing pdb.set_trace() into an IPython interactive prompt, which isn't a very useful thing to do, so the simplest way to avoid the problem is to just not do that. If you want to do it anyway, you can enter the r command a few times until pdb says you're out of IPython's guts. (Don't overshoot, or you'll end up in a different part of IPython's guts.)

The second problem is an essentially unavoidable interaction of heavily entrenched language design decisions. Unfortunately, it's unlikely to go away. List comprehensions in a debugger only work in a global scope, not while debugging a function. If you want to build a list while debugging a function, the easiest way is probably to use the interact command and write a for loop.


Here's the full combination of effects going into this.

  1. pdb.set_trace() triggers pdb on the next trace event, not at the point where pdb.set_trace() is called.

The trace function mechanism used by pdb and other Python debuggers only triggers on certain specific events, and "when a trace function is set" is unfortunately not one of those events. Normally, the next event is either a 'line' event for the next line or a 'return' event for the end of the current code object's execution, but that's not what happens here.

  1. IPython sets a displayhook to customize expression statement handling.

The mechanism Python uses to display the result of expression statements is sys.displayhook. When you do 1+2 at the interactive prompt:

>>> 1+2
3

sys.displayhook is what prints the 3 instead of discarding it. It also sets _. When the result of an expression statement is None, such as with the expression pdb.set_trace(), sys.displayhook does nothing, but it's still called.

IPython replaces sys.displayhook with its own custom handler, responsible for printing the Out[n]: thingy, for setting entries in the Out record, for calling IPython custom pretty-printing, and for all sorts of other IPython conveniences. For our purposes, the important thing is that IPython's displayhook is written in Python, so the next trace event is a 'call' event for the displayhook.

pdb starts debugging inside IPython's 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):

  1. List comprehensions create a new scope.

People didn't like how list comprehensions leaked the loop variable into the containing scope in Python 2, so list comprehensions get their own scope in Python 3.

  1. pdb uses eval, which interacts really poorly with closure variables.

Python's closure variable mechanism relies on static scope analysis that's completely incompatible with how eval works. Thus, new scopes created inside eval have no access to closure variables; they can only access globals.


Putting it all together, in IPython, you end up debugging the IPython displayhook instead of the scope you're running interactive code in. Since you're inside IPython's displayhook, your x = 1 assignment goes into the displayhook's locals. The subsequent list comprehension would need access to the displayhook's locals to access x, but that would be a closure variable to the list comprehension, which doesn't work with eval.

Outside of IPython, sys.displayhook is written in C, so pdb can't enter it, and there's no 'call' event for it. You end up debugging the scope you intended to debug. Since you're in a global scope, x = 1 goes in globals, and the list comprehension can access it.

You would have seen the same effect if you tried to run x = 1; [x for i in range(3)] while debugging any ordinary function.

这篇关于iPython 调试器引发 `NameError: name ... is not defined`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆