Ipython: 嵌入式ipython shell交互使用中未定义全局变量

创建于 2010-05-10  ·  39评论  ·  资料来源: ipython/ipython

原始启动板错误399627: https ://bugs.launchpad.net/ipython/+bug/399627
报告人:h-fangohr(Hans Fangohr)。

该错误可以重现如下:

  1. 启动Python,然后启动嵌入式ipython会话。 按照ipython的手册,我们做
fangohr<strong i="11">@eta</strong>:~$ python
Python 2.4.3 (#1, Jun  8 2009, 14:09:06) 
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from IPython.Shell import IPShellEmbed
>>> ipshell=IPShellEmbed()
>>> ipshell()

在刚开始的ipython会话中,全局变量有时不可见。 两个示例是:

范例1:

In [1]: a=1

In [2]: def f(x):
   ...:     return a*x
   ...: 

In [3]: f(2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/fangohr/<ipython console> 

/home/fangohr/<ipython console> in f(x)

NameError: global name 'a' is not defined

范例2:

In [4]: b=1

In [5]: (lambda :b)()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/fangohr/<ipython console> 

/home/fangohr/<ipython console> in <lambda>()

NameError: global name 'b' is not defined

如果在脚本中放入“ b = 1;(lambda:b)()”,则没有错误,并且该脚本使用ipython的“ run”命令执行。

如果启动ipython之后以交互方式执行“ b = 1;(lambda:b)()”,则没有错误。

出现该错误的唯一方法是,如果从Python提示符下启动了嵌入式IPython shell,并且在提示符下以交互方式执行命令。

我在尝试使用python2.4的ipython-0.9.1时发现了相同的错误。

该错误由Olivier Klein报告给了nmag小组(http://nmag.soton.ac.uk)。

最有用的评论

https://github.com/inducer/pudb/issues/103所述,使用嵌入式外壳程序时的临时解决方法是: globals().update(locals()) (仅在定义局部全局变量之后)。

所有39条评论

[LP评论1,作者:Fernando Perez,在2010-04-25 23:36:38.673176 + 00:00]

好的,我可以确认问题(即使在后备箱上),但是很难。 我现在只是确保已确认此错误,因此我们会继续对其进行跟踪,但是我不确定如何修复它。

问题在于,在嵌入式外壳程序中,我们尝试更新全局名称空间以使用周围的范围(这是嵌入式外壳程序的关键,以便能够看到您周围的内容)。 但是,这会导致在定义嵌套事物(例如本地函数)时,python无法解析ipython交互式名称空间。 有关详细信息,请参见嵌入式外壳程序的mainloop()方法。

我需要更多地思考如何解决这一问题,非常欢迎任何想法。

在启动板...

主席K在20小时前写道:

我也是。 除功能外,此错误还出现在生成器表达式中。

中继中的等效组件似乎是IPython.frontend.terminal.InteractiveShellEmbed。 但这在其他方面已被打破,而且显然没有经过太多测试。 有谁知道它的未来?

这可能与#136问题相同吗?

现在已修复:

amirbar[ipython]> python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from IPython import embed
>>> embed()
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12.dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: a = 1

In [2]: def f(x):
   ...:     return a*x
   ...: 

In [3]: f(3)
Out[3]: 3

In [4]: b = 1

In [5]: (lambda : b)()
Out[5]: 1

In [6]: 

有人可以解释一下解决此问题的方法吗? 我仍然遇到问题,但是仅当我通过方法调用删除了一层,并且仅当我从脚本运行而不是交互式地运行时。

OSX 10.6.8上的python 2.7.2,ipython 0.11和0.12都表现出相似的行为(对此注释使用0.12)

这是我们项目的一个问题,该项目(主要)具有嵌入式IPython shell。

testembed.py

from IPython import embed

def hi():
    embed()

if __name__ == '__main__':
    #embed()
    hi()

在命令行上使用python testembed.py运行此命令,并查看此会话:

Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import time

In [2]: def tim():
   ...:     print time.time()
   ...:     

In [3]: tim()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
[snip] in <module>()
----> 1 tim()

[snip] in tim()
      1 def tim():
----> 2     print time.time()
      3 

NameError: global name 'time' is not defined

In [4]: 

但是,请注释掉对hi()的调用并将其替换为embed()调用:

Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import time

In [2]: def tim():
    print time.time()
   ...:     

In [3]: tim()
1328639444.29

In [4]: 

在四处寻找之后,我认为这与此处使用的stack_depth参数和以下代码块有关: https :

有什么想法吗?

这有些复杂,但是我认为这是Python本身的限制。

在失败的情况下,您实际上并没有将time放入全局名称空间:因为您在hi函数中调用了embed ,所以您以交互方式创建的新变量是本地的功能。 理想情况下, tim()应该作为闭包工作,关闭对时间模块的引用。 但是,闭包仅在一次性编译包含函数时才起作用。 据我所知,没有办法动态定义闭包。 这个简单的例子失败了:

def outer():
    import time
    exec("def inner(): return time.time()")
    return inner

outer()()

这可能是因为嵌套作用域相对较晚地添加到了Python中(它们是将来在2.1中导入的,并且始终在2.2中启用)。

好吧,我想我明白了。 看起来我们真的不能做任何事情,除了将我交互式编写的内容转储到文件中然后再读回该文件。对于我们希望能够交互式执行的操作而言,可能太复杂了。

谢谢,顺便说一句。

很抱歉继续在这里讲话,但这只会使交互式会话感觉很笨拙:

常规python:

>>> d={'one':1, 'two':2}
>>> getkeys=lambda: d.keys()
>>> getkeys()
['two', 'one']

常规IPython:

In [1]: d={'one':1, 'two':2}

In [2]: getkeys=lambda: d.keys()

In [3]: getkeys()
Out[3]: ['two', 'one']

嵌入式IPython:

>>> from IPython import embed
>>> embed()
Python 2.7.2 (default, Aug 29 2011, 12:33:18) 
Type "copyright", "credits" or "license" for more information.

IPython 0.12.dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: d={'one':1, 'two':2}

In [2]: getkeys=lambda: d.keys()

In [3]: getkeys()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/Users/asadeveloper/Documents/Dev/code/pyon/bin/python in <module>()
----> 1 getkeys()

/Users/asadeveloper/Documents/Dev/code/pyon/bin/python in <lambda>()
----> 1 getkeys=lambda: d.keys()

NameError: global name 'd' is not defined

我想没有什么可以做的,但是我不明白为什么我们可以在常规python / ipython中创建动态闭包,而不是嵌入式版本。

由于Python作用域的奇怪之处,在标准Python / IPython中,我们实际上并未创建闭包。 d是全局变量,访问这些规则的规则与闭包不同。 每个函数都保留对定义它的全局命名空间的引用( getkeys.func_globals ),因此它可以访问在那里定义的任何变量。

相反,当您进行闭包时,Python会将引用附加到它已关闭的每个变量上,这由编译器确定-但仅当同时编译内部函数和外部函数时,该函数才起作用。 看起来像这样:

In [8]: def outer():
    a = 1
    def inner():
        return a
    return inner
   ...: 

In [9]: f = outer()

In [10]: f
Out[10]: <function __main__.inner>

In [11]: f.func_closure
Out[11]: (<cell at 0x9f5e344: int object at 0x9a830b0>,)

这样做可能是为了节省内存-如果闭包中包含对定义它的本地作用域的引用,则在该闭包处于活动状态时,不能释放该作用域中的任何变量。

我在OSX 10.7.3上使用Python 2.7.2和IPython 0.12.1,仍然存在此问题。 当我运行./manage.py shell的Django的一个调用IPython.embed()时,会出现问题。 但是,在Python Shell或简单的脚本文件中手动调用embed()不会有问题。

我们可以直接做很多事情,但是我们应该鼓励第三方不要将embed用于非平凡用途。

@takluyver您的意思是在这种情况下直接使用ipython更好吗?

Django可能以不会引起此问题的方式启动IPython,但这不是我们当前易于实现的方式。 当IPython以单独的本地和全局名称空间开头时,就会发生此问题。 Django没有理由需要单独的本地和全局名称空间,但这就是在函数中调用embed()的含义。

供参考,这是Django中的代码:
https://code.djangoproject.com/browser/django/trunk/django/core/management/commands/shell.py

@takluyver,这很有意义,谢谢! 我为此打开了一张Django票。

@takluyver ,现在我们已经合并了embed_kernel,因此所有主要部分都就位了,您是否想解决一下此问题以使更精细的使用变得更容易?

我将看看哪种接口最有意义。

我在这个问题上仍然遇到麻烦。 我曾尝试缩小导致问题的原因,我能确定的最好办法是,它仅在Ubuntu 12.04上发生。 我已经在其他服务器上尝试了所有最新发行版本,并且工作正常。 但是,每当我在ipython中定义一个函数或使用%cpaste从另一个文件中粘贴一个函数时,该函数的内部都无法访问全局范围。 就动态地编写函数而言,这使得基本上不可能做任何有用的事情。

从其他工具(例如Django的debugsqlshell命令)调用IPython 0.13时,我仍然遇到此问题。 令人沮丧的是,像定义函数这样的基本操作完全被破坏了。

我认为embed()是使用这些接口的错误接口。 embed()是
旨在更多地检查正在运行的程序的状态,因此它使用
单独的本地和全局名称空间。 为了解决这个问题,ipython
需要从单个界面开始。 对不起,我还没来得及
找出最好的方法是什么。

不仅是ubuntu。 debian wheezy也表明了这一点。

仅供参考,上面创建的Django票证@liokmhttps://code.djangoproject.com/ticket/18204 ,现在指向https://code.djangoproject.com/ticket/17078 ,它似乎已在树干中修复。 它应该降落在1.5。

我在使用Ipython 0.13.2的Ubuntu上遇到相同的问题
screenshot from 2013-08-07 18 13 33

在Django 1.6中修复的@bkvirendra

但是1.6甚至还不稳定!

但是1.6甚至还不稳定!

软件并不总是稳定的,并且在发行版之间仍然可能存在错误。 但是在IPython中没有什么应该修复的。 即使我们在这里做某事,修复也不会在IPython中发布之前稳定下来。

问题仍然存在于ipython == 4.2.0中。 有趣的是,在Windows下这没问题,但是无法识别ubuntu全局变量。

用例:

from ipywidgets import interact, FloatSlider, IntSlider,RadioButtons, Dropdown

@interact(sv1 = radio_1, Scenario = drop_1, sv2 = slider_2)
def update_map(sv1,sv2, Scenario):

问题仍然存在。 IPython 3.1.0,Debian Whezzy。

如果我没记错的话,此问题已修复了一段时间,并且似乎已重新引入(在MacOS上使用IPython 5.1.0进行了复制,网址为:https://git.io/vPDrJ)。

(编辑:我可能记错了此修复程序。有可能我只是将所需的所有内容扔到了启动文件中,而不是使用嵌入)

以防万一这可以帮助我一直在使用这段代码作为解决方法的任何人

    def fix_embed_globals(N=0):
        # Get the parent frame
        import inspect
        frame_level0 = inspect.currentframe()
        frame_cur = frame_level0
        N = 2  # I may have this value off by one. I rewrote this function to use only standard calls
        strict = False
        for _ix in range(N + 1):
            frame_next = frame_cur.f_back
            if frame_next is None:
                if strict:
                    raise AssertionError('Frame level %r is root' % _ix)
                else:
                    break
            frame_cur = frame_next
        parent_frame = frame_cur
        # Add locals to parent globals
        parent_frame.f_locals.update(parent_frame.f_globals)
        parent_frame.f_globals['_didfixipyglobals'] = True

每当我得到名称错误时,我都只是调用该函数。 它将当前框架中的所有本地人放入全局词典中。 它很笨拙,但在大多数情况下都可以使用。

https://github.com/inducer/pudb/issues/103所述,使用嵌入式外壳程序时的临时解决方法是: globals().update(locals()) (仅在定义局部全局变量之后)。

嗯,这个错误的里程碑可以更改为下一个版本吗?

做完了

这个会解决吗?

AFAIK,我几年前的评论是:

  1. 我相信我们受到Python本身工作方式的限制。 闭包和动态评估不能很好地配合使用,而IPython无法修复它。 人们已经找到了将局部变量移动到全局名称空间的解决方法,但是这些都是会引起其他问题的黑客,因为他们正在修改全局名称空间。 我不打算将这样的解决方法放入IPython。 我宁愿留下我们未做的事情的问题,也不愿尝试过分聪明。

  2. 许多人以为他们想要embed() ,实际上应该改用start_ipython()来避免这种问题。

我不明白,这让我很生气。

抱歉,我感到沮丧的是,如果您将它们键入到一个空的.py文件中,那么这些东西将无法正常工作

但是重新阅读历史记录,似乎这是Django的manage.py shell一个问题,尤其是在我使用ipython的情况下,有90%的时间这样做,但是很容易忘记这种情况

令人尴尬的是,我正在使用过时的Django(1.4)进行大量工作

根据之前的评论,较新版本的Django Shell使用ipython的方式有所不同,可能没有问题吗? 例如https://code.djangoproject.com/ticket/17078

对于误解表示歉意

是的,我认为它对于Django 1.6是固定的。 如果您确实无法升级,则可能需要手动应用此修复程序。 看起来像这样: https :

我今天发现了一种解决方法,发布在#10695此线程显示了一种针对IPython未嵌入函数内部的情况的更简单处理。 我不是专家,所以请帮助检查有效性。

此页面是否有帮助?
0 / 5 - 0 等级