如何通过IPython的嵌入使输入/关闭工作?

时间:2016-02-02 18:37:29

标签: python import closures ipython

我有时会在脚本中的某个位置使用Sub Sample() Dim oRange As Range, aCell As Range, bCell As Range Dim ws As Worksheet Dim ExitLoop As Boolean Dim SearchString As String, FoundAt As String On Error GoTo Err Set ws = Worksheets("Sheet3") Set oRange = ws.Columns(1) SearchString = "2" Set aCell = oRange.Find(What:=SearchString, LookIn:=xlValues, _ LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _ MatchCase:=False, SearchFormat:=False) If Not aCell Is Nothing Then Set bCell = aCell FoundAt = aCell.Address Do While ExitLoop = False Set aCell = oRange.FindNext(After:=aCell) If Not aCell Is Nothing Then If aCell.Address = bCell.Address Then Exit Do FoundAt = FoundAt & ", " & aCell.Address Else ExitLoop = True End If Loop Else MsgBox SearchString & " not Found" End If MsgBox "The Search String has been found these locations: " & FoundAt Exit Sub Err: MsgBox Err.Description End Sub 来快速充实某些本地功能。最小的例子:

embed

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

#!/usr/bin/env python

# ...

import IPython
IPython.embed()

这相当令人困惑,特别是因为我甚至可以使用制表符完成来编写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

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

os.path.sep

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

这个问题有解决办法吗?

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

免责声明:我会自己发布一个(不满意的)问题答案 - 但仍然希望找到更好的解决方案。

2 个答案:

答案 0 :(得分:1)

更新:这只是一种解决方法,但有点简单: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

我没有通用的解决方案,但至少有一个解决办法:在定义本地函数之后,可以将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 问题。

答案 1 :(得分: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.init_frame(frame)之后调用shell = InteractiveShellEmbed.instance(...)

这是基于以下观察结果:

  • 在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()相同。

使用此设置,在不使用默认嵌入式外壳程序的情况下,以下代码应该可以工作:

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),并坚持在全局范围内查找变量。搜索closure in this page