代码之家  ›  专栏  ›  技术社区  ›  Bill

ipython调试器引发“nameerror:name…未定义”`

  •  4
  • Bill  · 技术社区  · 6 年前

    我无法理解此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
    

    为什么有时候它会告诉我 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()
    

    我使用python 3.6在pycharm中运行。

    修订更新:

    经过更多的测试,在 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
    

    但在普通的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]
    

    我在上面的3.4、3.5、3.6版本中进行了测试,因此它似乎不依赖于版本。

    更新2

    请注意,上面的测试(“修正更新”)有问题,因为它使用 import pdb; pdb.set_trace() 在交互式回复中。

    另外,最初的问题并不局限于ipython。

    answer by user2357112 下面是对这里发生的事情的全面解释。

    对不起,如果我引起了混乱!

    1 回复  |  直到 6 年前
        1
  •  1
  •   user2357112    6 年前

    你有两个核心问题。首先是(打电话时 pdb.set_trace() 在ipython中交互地)您正在调试ipython的勇气,而不是您想要的范围。第二,列表理解作用域规则与封闭作用域中存在的变量不能静态确定的情况(如在调试器或 class bodies .

    第一个问题几乎只在输入时发生 设置跟踪() 进入一个ipython交互提示,这不是一件很有用的事情,所以避免这个问题的最简单的方法就是不要这样做。如果您仍然想这样做,可以输入 r 命令几次直到PDB说你没勇气了。(不要过度,否则你会在伊普顿的肠子里找到另一个地方。)

    第二个问题本质上是根深蒂固的语言设计决策不可避免的交互作用。不幸的是,它不太可能消失。调试器中的列表理解只能在全局范围内工作,而不能在调试函数时工作。如果要在调试函数时生成列表,最简单的方法可能是使用 interact 命令并写入 for 回路。


    这是所有影响的综合。

    1. 设置跟踪() 下一次触发PDB 跟踪事件 ,而不是在 设置跟踪() 被称为。

    这个 trace function pdb和其他python调试器使用的机制仅在某些特定事件上触发,“当设置跟踪函数”不幸不是这些事件之一。通常,下一个事件是 'line' 下一行的事件或 'return' 当前代码对象执行结束时的事件,但此处不会发生这种情况。

    1. 伊普顿设置了一个 displayhook 自定义表达式语句处理。

    python用来显示表达式语句结果的机制是 sys.displayhook . 当你这样做的时候 1+2 在交互提示下:

    >>> 1+2
    3
    

    系统显示挂钩 是什么印出了 3 而不是抛弃它。它也设置 _ . 当表达式语句的结果是 None ,例如 设置跟踪() , 系统显示挂钩 什么都不做,但还是叫它。

    伊普顿取代 系统显示挂钩 具有自己的自定义处理程序,负责打印 Out[n]: thingy,用于设置 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):
    
    1. 列表理解创建一个新范围。

    人们不喜欢列表理解是如何将循环变量泄漏到python 2的包含范围中的,因此列表理解在python 3中获得了自己的范围。

    1. PDB使用 eval ,它与闭包变量的交互非常差。

    python的闭包变量机制依赖于静态范围分析,这与 评价 作品。因此,在内部创建了新的作用域 埃瓦 无法访问闭包变量;它们只能访问全局变量。


    总之,在ipython中,最终调试的是ipython displayhook,而不是运行交互代码的作用域。既然你在ipython的displayhook里, x = 1 赋值进入displayhook的局部。接下来的列表理解需要访问displayhook的本地变量才能访问 x ,但这将是列表理解的一个闭包变量,它不适用于 埃瓦 .

    在伊普顿城外, 系统显示挂钩 是用C写的,所以PDB不能输入,而且没有 “呼叫” 它的事件。最终调试的是要调试的作用域。既然你在全球范围内, x=1 进入全局,列表理解可以访问它。

    如果你想逃跑你也会看到同样的效果 x = 1; [x for i in range(3)] 调试任何普通函数时。