如何卸载(重新加载)Python模块?

How do I unload (reload) a Python module?

我有一个长期运行的python服务器,希望能够在不重新启动服务器的情况下升级服务。最好的方法是什么?

1
2
3
4
if foo.py has changed:
    unimport foo  <-- How do I do this?
    import foo
    myfoo = foo.Foo()


当模块已经导入时,可以使用reload内置函数重新加载模块:

1
2
3
4
5
6
7
from importlib import reload  # Python 3.4+ only.
import foo

while True:
    # Do some things.
    if is_changed(foo):
        foo = reload(foo)

在python3中,reload被移动到imp模块。在3.4中,imp被否决,赞成importlibreload被添加到后者中。当目标为3或更高版本时,在调用reload时引用适当的模块或导入它。

我想这就是你想要的。像Django的开发服务器这样的Web服务器使用它,这样您就可以看到代码更改的效果,而无需重新启动服务器进程本身。

从文件中引用:

Python modules’ code is recompiled and
the module-level code reexecuted,
defining a new set of objects which
are bound to names in the module’s
dictionary. The init function of
extension modules is not called a
second time. As with all other objects
in Python the old objects are only
reclaimed after their reference counts
drop to zero. The names in the module
namespace are updated to point to any
new or changed objects. Other
references to the old objects (such as
names external to the module) are not
rebound to refer to the new objects
and must be updated in each namespace
where they occur if that is desired.

正如您在问题中提到的,如果Foo类驻留在Foo模块中,则必须重建Foo对象。


在python 3.0–3.3中,您将使用:imp.reload(module)

BDFL已经回答了这个问题。

但是,在3.4中,imp被否决,支持importlib(谢谢@stefan!).

因此,我认为你现在应该使用importlib.reload(module),尽管我不确定。


如果一个模块不是纯Python,那么删除它可能会特别困难。

以下是一些信息:如何真正删除导入的模块?

You can use sys.getrefcount() to find out the actual number of
references.

1
2
3
4
5
6
7
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3

Numbers greater than 3 indicate that
it will be hard to get rid of the
module. The homegrown"empty"
(containing nothing) module should be
garbage collected after

1
2
>>> del sys.modules["empty"]
>>> del empty

as the third reference is an artifact
of the getrefcount() function.


reload(module),但前提是它是完全独立的。如果有任何其他内容引用了模块(或属于模块的任何对象),那么由于旧代码挂起的时间比您预期的要长,并且像isinstance这样的东西不能在同一代码的不同版本中工作,您将得到微妙而奇怪的错误。

如果您有单向依赖项,则还必须重新加载依赖于重新加载的模块的所有模块,以除去对旧代码的所有引用。然后递归地重新加载依赖于重新加载的模块的模块。

如果您有循环依赖项,这是非常常见的,例如在处理重新加载包时,必须一次性卸载组中的所有模块。使用reload()不能这样做,因为它将在刷新依赖项之前重新导入每个模块,从而允许旧引用进入新模块。

在这种情况下,唯一的方法是黑客sys.modules,这是不受支持的。在下一次导入时,您必须浏览并删除每个要重新加载的sys.modules条目,还必须删除值为None的条目,以处理与缓存失败的相对导入有关的实现问题。它不是非常好,但是只要您有一组完全独立的依赖项,并且不将引用放在其代码基之外,它是可行的。

最好重新启动服务器。-)


1
2
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]


对于python 2,使用内置函数reload():

1
reload(module)

对于python 2和3.2–3.3,使用module imp中的reload:

1
2
import imp
imp.reload(module)

但由于版本3.4支持importlib,因此不推荐使用imp,因此使用:

1
2
import importlib
importlib.reload(module)

1
2
from importlib import reload
reload(module)


以下代码允许您与python 2/3兼容:

1
2
3
4
5
try:
    reload
except NameError:
    # Python 3
    from imp import reload

在这两个版本中,您可以将它用作reload(),这使事情变得更简单。


接受的答案不处理"从X导入Y"案例。此代码还处理它和标准导入案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
def importOrReload(module_name, *names):
    import sys

    if module_name in sys.modules:
        reload(sys.modules[module_name])
    else:
        __import__(module_name, fromlist=names)

    for name in names:
        globals()[name] = getattr(sys.modules[module_name], name)

# use instead of: from dfly_parser import parseMessages
importOrReload("dfly_parser","parseMessages")

在重新加载的情况下,我们将顶级名称重新分配给存储在新重新加载的模块中的值,该模块将更新这些值。


这是重新加载模块的现代方法:

1
from importlib import reload

只需键入reload(MODULE_NAME),将MODULE_NAME替换为要重新加载的模块的名称。

例如,reload(math)将重新加载数学函数。


如果您不在服务器中,但是正在开发并需要频繁地重新加载模块,那么这里有一个很好的提示。

首先,确保你使用的是优秀的IPython外壳,来自Jupyter笔记本项目。安装jupyter之后,您可以从ipythonjupyter console或更好的jupyter qtconsole开始,这将在任何操作系统中为您提供具有代码完成功能的彩色控制台。

现在在shell中,键入:

1
2
%load_ext autoreload
%autoreload 2

现在,每次运行脚本时,都会重新加载模块。

2外,还有其他自动加载的选项:

1
2
3
4
5
6
7
8
9
10
11
12
%autoreload
Reload all modules (except those excluded by %aimport) automatically now.

%autoreload 0
Disable automatic reloading.

%autoreload 1
Reload all modules imported with %aimport every time before executing the Python code typed.

%autoreload 2
Reload all modules (except those excluded by %aimport) every time before
executing the Python code typed.

对于像我这样希望卸载所有模块的人(在Emacs下的python解释器中运行时):

1
2
   for mod in sys.modules.values():
      reload(mod)

更多信息在重新加载python模块中。


热情特性有一个模块,可以很好地解决这个问题。https://traits.readthedocs.org/en/4.3.0//u modules/traits/util/refresh.html

它将重新加载任何已更改的模块,并更新使用它的其他模块和实例对象。它在大多数情况下不适用于__very_private__方法,并且可能会阻塞类继承,但它为我节省了大量的时间,使我不必在编写pyqt gui或在程序(如maya或nuke)中运行的东西时重新启动宿主应用程序。它可能在20-30%的时间里不起作用,但它仍然非常有用。

Enthould的包在文件更改时不会重新加载文件—您必须明确地调用它—但如果您真的需要它,实现起来并不那么困难。


使用python 3并从importlib重新加载的用户。

如果您遇到模块似乎无法重新加载的问题…这是因为它需要一些时间来重新编译pyc(最长60秒),我编写这个提示只是为了让您知道您是否遇到过这种问题。


2018~02-01

  • 模块foo必须提前导入成功。
  • 江户十一〔9〕、江户十一〔10〕。
  • 31.5。importlib-import的实现-python 3.6.4文档


    其他选择。请注意,python的默认importlib.reload将只重新导入作为参数传递的库。它不会重新加载lib导入的库。如果您更改了很多文件,并且要导入的包有些复杂,那么必须进行深度重新加载。

    如果安装了IPython或Jupyter,则可以使用函数来深度重新加载所有libs:

    1
    2
    from IPython.lib.deepreload import reload as dreload
    dreload(foo)

    如果没有jupyter,请在shell中使用以下命令安装它:

    1
    pip3 install jupyter


    对于我来说,Abaqus就是这样工作的。假设您的文件是class_verticesedges.py

    1
    2
    3
    4
    5
    6
    sys.path.append('D:\...\My Pythons')
    if 'Class_VerticesEdges' in sys.modules:  
        del sys.modules['Class_VerticesEdges']
        print 'old module Class_VerticesEdges deleted'
    from Class_VerticesEdges import *
    reload(sys.modules['Class_VerticesEdges'])


    我在尝试重新加载Sublime文本中的内容时遇到了很多问题,但最终我可以编写这个实用程序,根据sublime_plugin.py用于重新加载模块的代码,在Sublime文本上重新加载模块。

    下面接受您从名称上带有空格的路径重新加载模块,然后在重新加载之后,您可以像通常那样导入模块。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    def reload_module(full_module_name):
       """
            Assuming the folder `full_module_name` is a folder inside some
            folder on the python sys.path, for example, sys.path as `C:/`, and
            you are inside the folder `C:/Path With Spaces` on the file
            `C:/Path With Spaces/main.py` and want to re-import some files on
            the folder `C:/Path With Spaces/tests`

            @param full_module_name   the relative full path to the module file
                                      you want to reload from a folder on the
                                      python `sys.path`
       """

        import imp
        import sys
        import importlib

        if full_module_name in sys.modules:
            module_object = sys.modules[full_module_name]
            module_object = imp.reload( module_object )

        else:
            importlib.import_module( full_module_name )

    def run_tests():
        print("

    "
    )
        reload_module("Path With Spaces.tests.semantic_linefeed_unit_tests" )
        reload_module("Path With Spaces.tests.semantic_linefeed_manual_tests" )

        from .tests import semantic_linefeed_unit_tests
        from .tests import semantic_linefeed_manual_tests

        semantic_linefeed_unit_tests.run_unit_tests()
        semantic_linefeed_manual_tests.run_manual_tests()

    if __name__ =="__main__":
        run_tests()

    如果您是第一次运行,那么应该加载模块,但是如果以后您可以再次使用方法/函数run_tests(),它将重新加载测试文件。对于升华文本(Python 3.3.6),这种情况经常发生,因为它的解释器从不关闭(除非重新启动升华文本,即Python3.3解释器)。


    另一种方法是在函数中导入模块。这样,当函数完成时,模块就会被垃圾收集。