如何从 IPython 的嵌入中导入/关闭?
How to make imports / closures work from IPython's embed?
我有时会在脚本的某个点使用 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
模块名称可能只是 "close over"...
最常见的东西
这个问题有什么解决办法吗?
更新:问题不仅适用于闭包,而且nested list comprehensions。
免责声明:我将 post 自己对问题给出一个(不令人满意的)答案——不过仍然希望有更好的解决方案。
更新:同样只是一个解决方法,但更简单一些: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]: '/'
但是,应该注意这只是一个 "manual closure",在这种情况下不能用作常规闭包:
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
进口问题。
我也遇到了同样的问题。我用这个技巧来处理在函数外部调用 embed()
的情况,因此 globals()
和 locals()
应该是同一个字典。
最简单的方法是在ipython启动后调用下面的函数
ipy = get_ipython()
setattr(ipy.__class__, 'user_global_ns', property(lambda self: self.user_ns))
另一种方法是subclass 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_ns
是 class 属性 的超 class InteractiveShellEmbed
,其中 returns ipy.user_module.__dict__
)
- 我们还有
id(locals()) == id(ipy.user_ns)
- 对于正常的 ipython 会话,
id(locals()) == id(globals())
user_global_ns
(一个属性)和user_ns
(一个dict)定义执行上下文
- 在嵌入式 ipython 中,
ipy.user_module
和 ipy.user_ns
在函数 ipy.__call__()
中设置并传递给 ipy.mainloop()
。它们不是同一个对象,因为 ipy.user_ns
是在函数内部构造的。
如果您要在函数外部(例如在脚本中)启动 ipython,那么可以安全地假设 globals()
应该与 locals()
.[=43 相同=]
使用此设置,以下代码在使用默认嵌入式 shell 不工作时应该可以工作:
a=3
(lambda :a)() # default behavior: name 'a' is not defined
import time
(lambda: time.time())() # default behavior: name 'time' is not defined
(默认行为是由于 a
和 time
未添加到 globals()
并且 ipython
不会对局部函数(上面定义的 lambda)进行闭包并坚持在全局范围内查找变量。搜索 closure
in this page)
我有时会在脚本的某个点使用 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
模块名称可能只是 "close over"...
最常见的东西这个问题有什么解决办法吗?
更新:问题不仅适用于闭包,而且nested list comprehensions。
免责声明:我将 post 自己对问题给出一个(不令人满意的)答案——不过仍然希望有更好的解决方案。
更新:同样只是一个解决方法,但更简单一些: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]: '/'
但是,应该注意这只是一个 "manual closure",在这种情况下不能用作常规闭包:
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
进口问题。
我也遇到了同样的问题。我用这个技巧来处理在函数外部调用 embed()
的情况,因此 globals()
和 locals()
应该是同一个字典。
最简单的方法是在ipython启动后调用下面的函数
ipy = get_ipython()
setattr(ipy.__class__, 'user_global_ns', property(lambda self: self.user_ns))
另一种方法是subclass 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_ns
是 class 属性 的超 classInteractiveShellEmbed
,其中 returnsipy.user_module.__dict__
) - 我们还有
id(locals()) == id(ipy.user_ns)
- 对于正常的 ipython 会话,
id(locals()) == id(globals())
user_global_ns
(一个属性)和user_ns
(一个dict)定义执行上下文- 在嵌入式 ipython 中,
ipy.user_module
和ipy.user_ns
在函数ipy.__call__()
中设置并传递给ipy.mainloop()
。它们不是同一个对象,因为ipy.user_ns
是在函数内部构造的。
如果您要在函数外部(例如在脚本中)启动 ipython,那么可以安全地假设 globals()
应该与 locals()
.[=43 相同=]
使用此设置,以下代码在使用默认嵌入式 shell 不工作时应该可以工作:
a=3
(lambda :a)() # default behavior: name 'a' is not defined
import time
(lambda: time.time())() # default behavior: name 'time' is not defined
(默认行为是由于 a
和 time
未添加到 globals()
并且 ipython
不会对局部函数(上面定义的 lambda)进行闭包并坚持在全局范围内查找变量。搜索 closure
in this page)