【问题标题】:How to make imports / closures work from IPython's embed?如何使导入/闭包从 IPython 的嵌入中工作?
【发布时间】:2016-02-02 18:37:28
【问题描述】:

我有时会在脚本的某个位置使用embed 来快速充实一些本地功能。最小的例子:

#!/usr/bin/env python

# ...

import IPython
IPython.embed()

开发本地函数通常需要新的导入。但是,当在函数中使用时,在 IPython 会话中导入模块似乎不起作用。例如:

In [1]: import os

In [2]: def local_func(): return os.path.sep

In [3]: local_func()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-f0e5d4635432> in <module>()
----> 1 local_func()

<ipython-input-2-c530ce486a2b> in local_func()
----> 1 def local_func(): return os.path.sep

NameError: global name 'os' is not defined

这相当令人困惑,尤其是因为我什至可以使用制表符完成来编写os.path.sep

我注意到问题更为根本:通常,在 IPython 嵌入会话中创建的函数不会关闭嵌入范围内的变量。例如,这也失败了:

In [4]: x = 0

In [5]: def local_func(): return x

In [6]: local_func()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-f0e5d4635432> in <module>()
----> 1 local_func()

<ipython-input-5-2116e9532e5c> in local_func()
----> 1 def local_func(): return x

NameError: global name 'x' is not defined

模块名称可能只是“关闭”最常见的东西......

有没有办法解决这个问题?

更新:问题不仅适用于闭包,也适用于nested list comprehensions

免责声明:我将自己发布一个(不令人满意的)问题答案——不过仍然希望有更好的解决方案。

【问题讨论】:

  • 截至 2017 年 2 月,我仍然观察到 sys.version 为 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609] 的问题。

标签: python import closures ipython


【解决方案1】:

我也遇到了同样的问题。我使用这个技巧来处理 embed() 在函数外被调用的情况,因此 globals()locals() 应该是同一个字典。

最简单的方法是在ipython启动后调用如下函数

ipy = get_ipython()
setattr(ipy.__class__, 'user_global_ns', property(lambda self: self.user_ns))

另一种方法是继承InteractiveShellEmbed

class InteractiveShellEmbedEnhanced(InteractiveShellEmbed):
    @property
    def user_global_ns(self):
        if getattr(self, 'embedded_outside_func', False):
            return self.user_ns
        else:
            return self.user_module.__dict__

    def init_frame(self, frame):
        if frame.f_code.co_name == '<module>':
            self.embedded_outside_func = True
        else:
            self.embedded_outside_func = False

并稍微修改IPython.terminal.embed.embed() 的代码,以便将其中的所有InteractiveShellEmbed 更改为InteractiveShellEmbedEnhanced,并在shell = InteractiveShellEmbed.instance(...) 行之后调用shell.init_frame(frame)

这是基于以下观察:

  • 在ipython会话中,我们总是有id(globals()) == id(ipy.user_module.__dict__) == id(ipy.user_global_ns)user_global_nsInteractiveShellEmbed的超类的类属性,它返回ipy.user_module.__dict__
  • 我们还有id(locals()) == id(ipy.user_ns)
  • 对于正常的 ipython 会话,id(locals()) == id(globals())
  • user_global_ns(属性)和user_ns(字典)定义执行上下文
  • 在嵌入式 ipython 中,ipy.user_moduleipy.user_ns 在函数 ipy.__call__() 中设置并传递给 ipy.mainloop()。它们不是同一个对象,因为ipy.user_ns 是在函数内部构造的。

如果您要在函数之外(例如在脚本中)启动 ipython,那么可以安全地假设 globals() 应该与 locals() 相同。

通过此设置,以下代码在使用默认嵌入式 shell 时应该可以正常工作:

a=3
(lambda :a)()    # default behavior: name 'a' is not defined
import time
(lambda: time.time())()  # default behavior: name 'time' is not defined

(默认行为是由于 atime 未添加到 globals()ipython 不会为本地函数(上面定义的 lambda)创建闭包并坚持在全局范围内查找变量.搜索closurein this page)

【讨论】:

  • 非常有趣。你认为将它贡献给 iPython 有意义吗?也许这可以解决问题#62#10695
  • @bluenote10 我不是 python 或 IPython 方面的专家。我不确定代码在函数外启动时是否在所有情况下都能正常工作。我只能分享我的代码,让专家告诉它是作为解决方案还是指向解决它的方向。我已经在你的#10695 中添加了一个更完整的 sn-p,它还提供了从函数嵌入 IPython 时的帮助器。
【解决方案2】:

更新:同样只是一种解决方法,但更简单:globals().update(locals())


我没有通用的解决方案,但至少有一个解决方法:定义本地函数后,可以将会话的locals() 添加到刚刚定义的函数的func_globals 中,例如:

In [1]: import os

In [2]: def local_func(): return os.path.sep

In [3]: local_func.func_globals.update(locals())

In [4]: local_func()
Out[4]: '/'

但是,应该知道这只是一种“手动关闭”,在这种情况下不能作为常规关闭:

In [1]: x = 1

In [2]: def local_func(): return x

In [3]: local_func.func_globals.update(locals())

In [4]: local_func()
Out[4]: 1

In [5]: x = 42

In [6]: local_func() # true closure would return 42
Out[6]: 1

In [7]: local_func.func_globals.update(locals()) # but need to update again

In [8]: local_func()
Out[8]: 42

至少它可以解决臭名昭著的global name '...' is not defined导入问题。

【讨论】:

    猜你喜欢
    • 2014-10-06
    • 1970-01-01
    • 2013-07-24
    • 1970-01-01
    • 1970-01-01
    • 2018-11-27
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    相关资源
    最近更新 更多