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

时间:2009-01-13 00:33:36

标签: python module reload python-import

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

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

20 个答案:

答案 0 :(得分:668)

使用reload内置函数导入模块后,可以重新加载模块:

from importlib import reload  # Python 3.4+ only.
import foo

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

在Python 3中,reload已移至imp模块。在3.4中,imp被弃用,而importlib被添加,reload被添加到后者中。定位3或更高版本时,请在调用reload时引用相应的模块或导入它。

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

引用文档:

  

重新编译Python模块的代码   重新执行模块级代码,   定义一组新的对象   绑定到模块中的名称   字典。的init函数   扩展模块不称为   第二次。与所有其他对象一样   在Python中只有旧对象   在他们的参考计数后回收   降到零。模块中的名称   命名空间更新为指向任何   新的或改变的对象。其他   对旧对象的引用(例如   模块外部的名称)不是   反弹以引用新对象   并且必须在每个命名空间中更新   如果需要,它们会在哪里出现。

正如您在问题中所述,如果Foo类位于Foo模块中,则必须重建foo个对象。

答案 1 :(得分:243)

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

BDFLanswered这个问题。

然而,imp was deprecated in 3.4, in favour of importlib(感谢@Stefan!)。

,因此,你现在使用importlib.reload(module),虽然我不确定。

答案 2 :(得分:84)

如果模块不是纯Python,则删除模块可能特别困难。

以下是来自How do I really delete an imported module?

的一些信息
  

您可以使用sys.getrefcount()来查找实际的数字   引用。

>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
  

大于3的数字表示   它很难摆脱   模块。本土的“空”   (不含任何东西)模块应该是   

之后收集的垃圾
>>> del sys.modules["empty"]
>>> del empty
  

因为第三个参考是一个工件   getrefcount()函数。

答案 3 :(得分:62)

reload(module),但前提是它完全独立。如果其他任何东西都有对模块(或属于该模块的任何对象)的引用,那么你会得到由旧代码挂起的时间超过预期而导致的细微和好奇的错误,以及isinstance之类的工作无法正常工作跨相同代码的不同版本。

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

如果您具有循环依赖关系(例如,在处理重新加载包时非常常见),则必须一次性卸载组中的所有模块。您无法使用reload()执行此操作,因为它会在刷新依赖项之前重新导入每个模块,从而允许旧引用进入新模块。

在这种情况下执行此操作的唯一方法是破解sys.modules,这是一种不受支持的行为。您必须浏览并删除下次导入时要重新加载的每个sys.modules条目,并删除值为None的条目,以处理与缓存失败的相对导入有关的实现问题。它并不是非常好,但只要你有一套完全自包含的依赖项,它不会将引用留在代码库之外,它就是可行的。

最好重启服务器。 : - )

答案 4 :(得分:61)

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

答案 5 :(得分:51)

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

reload(module)

对于Python 2和3.2-3.3,请使用reload from module imp

import imp
imp.reload(module)

但自版本3.4 is deprecated以来imp in favor of importlib,请使用:

import importlib
importlib.reload(module)

from importlib import reload
reload(module)

答案 6 :(得分:21)

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

try:
    reload
except NameError:
    # Python 3
    from imp import reload

您可以在两个版本中将其用作reload(),这样可以简化操作。

答案 7 :(得分:16)

接受的答案并不处理来自X导入Y的情况。此代码也处理它和标准导入案例:

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")

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

答案 8 :(得分:14)

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

from importlib import reload

如果您想支持3.4以上版本的Python,请尝试以下方法:

from sys import version_info
if version_info[0] < 3:
    pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
    from imp import reload # Python 3.0 - 3.4 
else:
    from importlib import reload # Python 3.5+

要使用它,请运行reload(MODULE),将MODULE替换为您要重新加载的模块。

例如,reload(math)将重新加载math模块。

答案 9 :(得分:10)

如果您在服务器中,但是开发并且需要经常重新加载模块,那么这是一个不错的提示。

首先,确保您使用的是Jupyter Notebook项目中的优秀IPython shell。安装Jupyter之后,您可以使用ipythonjupyter console,甚至更好的jupyter qtconsole启动它,这将为您提供一个漂亮的彩色控制台,在任何操作系统中都可以完成代码。

现在在shell中输入:

%load_ext autoreload
%autoreload 2

现在,每次运行脚本,您的模块将被重新加载。

除了2之外,还有其他options of the autoreload magic

%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.

答案 10 :(得分:7)

对于那些想要卸载所有模块的人(当在Emacs下的Python解释器中运行时):

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

更多信息位于 Reloading Python modules

答案 11 :(得分:5)

Enthought Traits有一个适用于此的模块。 https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html

它将重新加载已更改的任何模块,并更新正在使用它的其他模块和实例化对象。它在大多数情况下都不能用__very_private__方法工作,并且可以阻止类继承,但它在编写PyQt guis时不必重新启动宿主应用程序,或者在程序内运行的东西,这节省了我的时间。作为Maya或Nuke。它可能在20-30%的时间内不起作用,但它仍然非常有用。

Enthought的软件包在它们改变的那一刻不会重新加载文件 - 你必须明确地调用它 - 但如果你确实需要的话,这应该不是那么难以实现

答案 12 :(得分:4)

那些使用python 3并从importlib重新加载的人。

如果你有类似的问题似乎模块没有重新加载...那是因为它需要一些时间来重新编译pyc(最多60秒)。我写这个提示只是你知道你是否经历过这种问题。

答案 13 :(得分:3)

2018年2月1日

  1. 模块foo必须提前成功导入。
  2. from importlib import reloadreload(foo)
  3. 31.5. importlib — The implementation of import — Python 3.6.4 documentation

答案 14 :(得分:3)

其他选择。看到Python默认importlib.reload将重新导入作为参数传递的库。 赢了重新加载lib导入的库。如果您更改了大量文件并且要导入一些复杂的程序包,则必须执行深度重新加载

如果您安装了IPythonJupyter,则可以使用函数深度重新加载所有库:

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

如果您没有Jupyter,请在shell中使用此命令安装它:

pip3 install jupyter

答案 15 :(得分:2)

编辑(Answer V2)

以前的解决方案仅适用于获取重置信息,但不会更改所有引用(多于reload但少于所需数量)。为了实际设置所有引用,我必须进入垃圾收集器,并在那里重写引用。现在它就像一种魅力!

请注意,如果GC已关闭,或者重新加载了不受GC监视的数据,则此将不会起作用。如果您不想弄乱GC,那么原始答案可能就足够了。

新代码:

import importlib
import inspect
import gc
from weakref import ref


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    # First, log all the references before reloading (because some references may be changed by the reload operation).
    module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)

    new_module = importlib.reload(module)
    _reset_item_recursively(module, module_tree, new_module)


def _update_referrers(item, new_item):
    refs = gc.get_referrers(item)

    weak_ref_item = ref(item)
    for coll in refs:
        if type(coll) == dict:
            enumerator = coll.keys()
        elif type(coll) == list:
            enumerator = range(len(coll))
        else:
            continue

        for key in enumerator:

            if weak_ref_item() is None:
                # No refs are left in the GC
                return

            if coll[key] is weak_ref_item():
                coll[key] = new_item

def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
    if grayed_out_item_ids is None:
        grayed_out_item_ids = set()

    item_tree = dict()
    attr_names = set(dir(item)) - _readonly_attrs
    for sub_item_name in attr_names:

        sub_item = getattr(item, sub_item_name)
        item_tree[sub_item_name] = [sub_item, None]

        try:
            # Will work for classes and functions defined in that module.
            mod_name = sub_item.__module__
        except AttributeError:
            mod_name = None

        # If this item was defined within this module, deep-reset
        if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
                or isinstance(sub_item, EnumMeta):
            continue

        grayed_out_item_ids.add(id(sub_item))
        item_tree[sub_item_name][1] = \
            _get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)

    return item_tree


def _reset_item_recursively(item, item_subtree, new_item):

    # Set children first so we don't lose the current references.
    if item_subtree is not None:
        for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():

            try:
                new_sub_item = getattr(new_item, sub_item_name)
            except AttributeError:
                # The item doesn't exist in the reloaded module. Ignore.
                continue

            try:
                # Set the item
                _reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
            except Exception as ex:
                pass

    _update_referrers(item, new_item)

原始答案

按照@bobince的答案所述,如果在另一个模块中已经存在对该模块的引用(尤其是如果它是通过as关键字(如import numpy as np导入的),则该实例将不会被覆盖。

在应用要求配置模块处于“纯洁状态”的测试时,这对我来说是一个很大的问题,因此我编写了一个名为reset_module的函数,该函数使用importlib的{​​{ 1}}函数并递归地覆盖所有已声明模块的属性。已通过Python 3.6版进行了测试。

reload

注意:请谨慎使用!在非外围模块(例如,定义外部使用的类的模块)上使用它们可能会导致Python内部出现问题(例如,酸洗/不酸洗问题)。

答案 16 :(得分:1)

对于我来说,Abaqus就是它的工作方式。 想象一下你的文件是Class_VerticesEdges.py

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'])

答案 17 :(得分:1)

从sys.modules中删除模块也需要删除'None'类型。

方法1:

import sys
import json  ##  your module

for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') or sys.modules[m] == None ]: del sys.modules[mod]

print( json.dumps( [1] ) )  ##  test if functionality has been removed

方法2,使用簿记条目来删除所有依赖项:

import sys

before_import = [mod for mod in sys.modules]
import json  ##  your module
after_import = [mod for mod in sys.modules if mod not in before_import]

for mod in [m for m in sys.modules if m in after_import or sys.modules[m] == None]: del sys.modules[mod]

print( json.dumps( [2] ) )  ##  test if functionality has been removed

可选,只是可以确定所有条目都已删除,如果您选择:

import gc
gc.collect()

答案 18 :(得分:0)

尝试在Sublime Text中重新加载某些内容时遇到了很多麻烦,但最后我可以编写此实用程序,根据代码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( "\n\n" ) 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() 重新加载模块重新加载Sublime Text上的模块。

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

run_tests()

如果你是第一次运行,这应该加载模块,但是如果以后你可以再次使用方法/函数Python 3.3.6它将重新加载测试文件。使用Sublime Text(Python3.3)会发生很多事情,因为它的解释器永远不会关闭(除非你重新启动Sublime Text,即{{1}}解释器)。

答案 19 :(得分:0)

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