在 IPython 中重新加载子模块

Posted

技术标签:

【中文标题】在 IPython 中重新加载子模块【英文标题】:Reloading submodules in IPython 【发布时间】:2011-07-18 20:31:03 【问题描述】:

目前我正在开发一个包含子模块并使用 numpy/scipy 的 python 项目。 Ipython 用作交互式控制台。不幸的是,我对我现在使用的工作流程不是很满意,我希望能得到一些建议。

在 IPython 中,框架由一个简单的import 命令加载。但是,经常需要更改框架的子模块之一中的代码。此时已经加载了一个模型,我使用 IPython 与之交互。

现在,框架包含许多相互依赖的模块,即当框架最初加载时,主模块正在导入和配置子模块。只有在使用reload(main_mod.sub_mod) 重新加载模块时,才会执行对代码的更改。这很麻烦,因为我需要使用完整路径单独重新加载所有更改的模块。如果reload(main_module) 也重新加载所有子模块,但不重新加载 numpy/scipy..

【问题讨论】:

您愿意详细说明However, it is often necessary to change code in one of the submodules of the framework. 那么为什么是necessary to change code?谢谢 @eat:框架在不断开发中,所以代码库也在不断变化。 【参考方案1】:

IPython 带有一些automatic reloading 魔法:

%load_ext autoreload
%autoreload 2

它会在每次执行新行之前重新加载所有更改的模块。它的工作方式与dreload 略有不同。有一些注意事项,请输入 %autoreload? 以查看可能出现的问题。


如果您想始终启用此设置,请修改您的 IPython 配置文件 ~/.ipython/profile_default/ipython_config.py[1] 并附加:

c.InteractiveShellApp.extensions = ['autoreload']     
c.InteractiveShellApp.exec_lines = ['%autoreload 2']

通过下面的评论感谢@Kos。

[1] 如果您没有文件~/.ipython/profile_default/ipython_config.py,则需要先调用ipython profile create。或者该文件可能位于$IPYTHONDIR

【讨论】:

我的~/.ipython/profile_default/ipython_config.py 中有c.InteractiveShellApp.extensions = ['autoreload']c.InteractiveShellApp.exec_lines = ['%autoreload 2'] 不过,这可能会影响性能,因此请谨慎使用。 只有在 Ipython shell 中按 Enter 时才会执行重新加载,通常不会引起注意。 这在调试包时工作得很好,那么 dreload 的目的是什么,似乎 dreload 侵入性太大,并且在加载一些像 matplotlib 这样的包时会打印错误。 如果你使用@Kos方法,请确保定义了c:c = get_config()【参考方案2】:

在 IPython 0.12(可能更早)中,你可以使用这个:

%load_ext autoreload
%autoreload 2

这与pv. 的回答基本相同,只是扩展名已重命名,现在使用%load_ext 加载。

【讨论】:

对于我在 Jupyter 实验室的这项工作,它足够短,可以放入所有需要它的笔记本中。【参考方案3】:

名为importlib 的模块允许访问导入内部。特别是提供importlib.reload()功能:

import importlib
importlib.reload(my_module)

%autoreload 不同,importlib.reload() 也会重置模块中设置的全局变量。在大多数情况下,这就是您想要的。

importlib 仅从 Python 3.1 开始可用。对于旧版本,您必须使用模块imp

我建议阅读importlib.reload() 的文档以获取此函数的所有警告列表(递归重新加载、保留旧对象定义的情况等...)。

【讨论】:

在过去的两年里,我多次在谷歌上搜索过这个问题,每次你的答案都是我正在寻找的完美答案。谢谢。【参考方案4】:

由于某种原因,%autoreloaddreload 似乎都不适用于 import code from one notebook to another 时的情况。只有普通的 Python reload 有效:

reload(module)

基于[1]。

【讨论】:

在 Python 3.4+ 中,您可以在 importlib 模块中找到 reload。见this question。 此方法在添加实例方法时起作用,与%autoreload 技术不同。有一个open bug report to add support to %autoreload for this.。【参考方案5】:

IPython 提供dreload() 以递归方式重新加载所有子模块。就个人而言,我更喜欢使用 %run() 魔法命令(尽管它不会执行深度重新加载,正如 John Salvatier 在 cmets 中指出的那样)。

【讨论】:

我认为(不幸的是) %run script.py 只会重新加载您正在调用的脚本,而不是它导入的包。如果您尝试调试正在构建的包,这可能会很痛苦。 注意。 dreload 在最近的 IPython(例如 IPython 6.0)中已被 deepreload 取代。【参考方案6】:

http://shawnleezx.github.io/blog/2015/08/03/some-notes-on-ipython-startup-script/

为了避免一遍又一遍地输入这些神奇的函数,可以将它们放在 ipython 启动脚本中(在 .ipython/profile_default/startup 下以 .py 后缀命名它。该文件夹下的所有 python 脚本将根据词法顺序加载),如下所示:

from IPython import get_ipython
ipython = get_ipython()

ipython.magic("pylab")
ipython.magic("load_ext autoreload")
ipython.magic("autoreload 2")

【讨论】:

如果您使用例如运行脚本,这似乎也有效。 %run script.py来自 IPython REPL【参考方案7】:

这个怎么样:

import inspect

# needs to be primed with an empty set for loaded
def recursively_reload_all_submodules(module, loaded=None):
    for name in dir(module):
        member = getattr(module, name)
        if inspect.ismodule(member) and member not in loaded:
            recursively_reload_all_submodules(member, loaded)
    loaded.add(module)
    reload(module)

import mymodule
recursively_reload_all_submodules(mymodule, set())

这应该有效地重新加载您提供给它的整个模块和子模块树。您也可以将此函数放在您的 .ipythonrc 中(我认为),以便每次启动解释器时都会加载它。

【讨论】:

看起来不错,但是,它可能没有涵盖使用from ... import ...import ... as 导入的模块或模块成员。至少这在终端上以交互方式工作时经常给我带来一些麻烦。我已经开始在 IPython 中使用存储的宏来执行必要的导入和设置以开始在预定义的状态下工作。 只要你导入的是一个模块,它实际上覆盖了from ... import ...import ... as。它唯一没有涵盖的是包中的模块,这些模块不是从它的__init__.py 文件中加载的。对于包,您可能可以检查模块的__path__ 属性是否为目录。如果是,遍历它并递归地导入所有你能找到的模块。我没有写这部分,因为作者没有要求包的解决方案。 确实这看起来不错。我考虑过这种可能性,同时我希望会有一些内置功能,即基于this。但是,我不清楚如何使用它。在我发布原始问题之前应该进行一些挖掘之后,我发现了这个extension。 您也可以使用pkgutil 获取包中的所有子模块,即使该包没有将子模块导入顶部模块。 ***.com/a/1707786/1243926 你也必须这样做:for module in sys.modules:【参考方案8】:

另一种选择:

$ cat << EOF > ~/.ipython/profile_default/startup/50-autoreload.ipy
%load_ext autoreload
%autoreload 2
EOF

在 Ubuntu 14.04 上的 ipython 和 ipython3 v5.1.0 上验证。

【讨论】:

【参考方案9】:

我重新加载的标准做法是在首次打开IPython 后结合这两种方法:

from IPython.lib.deepreload import reload
%load_ext autoreload
%autoreload 2

在执行此操作之前加载模块将导致它们不会被重新加载,即使使用手动 reload(module_name)。我仍然很少遇到我尚未研究的类方法无法重新加载的莫名其妙的问题。

【讨论】:

【参考方案10】:

我不想为长线程添加另一个答案,但我找到了一个解决方案,可以在 %run() 上递归重新加载子模块,其他人可能会觉得有用(无论如何我都有)

del 您希望在 iPython 中从 sys.modules 运行时重新加载的子模块:

In[1]: from sys import modules
In[2]: del modules["mymodule.mysubmodule"] # tab completion can be used like mymodule.<tab>!

现在您的脚本将递归地重新加载这个子模块:

In[3]: %run myscript.py

【讨论】:

【参考方案11】:

请注意,如果您手动保存更改的文件(例如使用 ctrl+s 或 cmd+s),上述autoreload 仅适用于 IntelliJ。它似乎不适用于自动保存。

【讨论】:

我确认 PyCharm 也是如此。【参考方案12】:

在您的模块导入之前包括这些行,其中第一个测试是否已经加载了自动重载扩展:

if 'autoreload' not in get_ipython().extension_manager.loaded:
    %load_ext autoreload
%autoreload 2

import sys
    .
    .
    .

【讨论】:

【参考方案13】:

在 Anaconda 上的 Jupyter Notebooks 上,这样做:

%load_ext autoreload
%autoreload 2

产生了消息:

自动重载扩展已经加载。要重新加载它,请使用: %reload_ext autoreload

看起来最好这样做:

%reload_ext autoreload
%autoreload 2

版本信息:

笔记本服务器的版本是 5.0.0,运行在: Python 3.6.2 |Anaconda, Inc.| (默认,2017 年 9 月 20 日,13:35:58)[MSC v.1900 32 位(英特尔)]

【讨论】:

【参考方案14】:

任何子对象都不会被这个重新加载,我相信你必须使用 IPython 的 deepreload。

【讨论】:

以上是关于在 IPython 中重新加载子模块的主要内容,如果未能解决你的问题,请参考以下文章

IPython中模块的自动重新加载[重复]

Angular 5 的子路由无法正常工作

从字符串变量导入模块

重新加载子视图(UICollectionview)

用动画重新加载子视图

Angular 9 嵌套延迟加载模块,带有嵌套路由器出口