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

如何卸载(重新加载)Python模块?,python,module,reload,python-import,Python,Module,Reload,Python Import,我有一个长期运行的Python服务器,希望能够在不重新启动服务器的情况下升级服务。做这件事最好的方法是什么 if foo.py has changed: unimport foo <-- How do I do this? import foo myfoo = foo.Foo() 如果foo.py已更改: unimport foo您可以使用内置函数(仅限Python 3.4+)在模块已经导入时重新加载模块: 在Python3中,reload被移动到模块中。在3.

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

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

unimport foo您可以使用内置函数(仅限Python 3.4+)在模块已经导入时重新加载模块:

在Python3中,
reload
被移动到模块中。在3.4中,
imp
被弃用,取而代之的是,并被添加到后者中。当目标设定为3或更高版本时,请在调用
重新加载
时参考相应的模块,或将其导入

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

引用文件:

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

正如您在问题中所指出的,如果
Foo
类驻留在
Foo
模块中,则必须重构
Foo
对象。

reload(module)
,但前提是它完全独立。如果其他任何东西引用了模块(或属于模块的任何对象),那么您将得到由旧代码比您预期的时间长而引起的微妙而奇怪的错误,以及类似于
isinstance
的内容无法跨同一代码的不同版本工作

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

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

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


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

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

以下是来自以下方面的一些信息:

您可以使用sys.getrefcount()来找出 参考资料

大于3的数字表示 这将是很难摆脱的 模块。土生土长的“空” (不包含任何内容)模块应为 垃圾收集后

因为第三个引用是一个工件 getrefcount()函数的


在Python 3.0–3.3中,您将使用:

政府有这个问题

不过,谢谢


因此,我认为您现在应该使用,尽管我不确定。

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


更多信息见。

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

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

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

接受的答案不处理从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")

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

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

对于Python2使用内置函数:


对于Python2和Python3.2-3.3使用:

导入imp
导入重新加载(模块)
对于Python≥3.4
imp
支持,因此使用以下方法:

导入导入库
导入库重新加载(模块)
或:

从导入lib导入重新加载
重新加载(模块)

TL;博士:

蟒蛇≥ 3.4:
importlib.reload(模块)

Python 3.2-3.3:
imp.reload(模块)


Python2:
reload(module)

Enthoud Traits有一个模块可以很好地实现这一点

它将重新加载任何已更改的模块,并更新正在使用它的其他模块和实例化对象。它在
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu私有
方法中大部分时间都不起作用,并且可能会因类继承而阻塞,但它为我节省了大量时间。它可能在20-30%的时间里不起作用,但它仍然非常有用


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

对于我来说,对于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'])

我在重新装弹时遇到了很多麻烦,所以
   for mod in sys.modules.values():
      reload(mod)
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]
try:
    reload
except NameError:
    # Python 3
    from imp import reload
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")
reload(module)
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'])
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()
from importlib import reload
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+
%load_ext autoreload
%autoreload 2
%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.
from IPython.lib.deepreload import reload as dreload
dreload(foo)
pip3 install jupyter
import importlib
import inspect
import gc
from enum import EnumMeta
from weakref import ref


_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


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)
import importlib
import inspect
from enum import EnumMeta

_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


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

    new_module = importlib.reload(module)

    reset_items = set()

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

    _reset_item_recursively(module, new_module, module.__name__, reset_items)


def _reset_item_recursively(item, new_item, module_name, reset_items=None):
    if reset_items is None:
        reset_items = set()

    attr_names = set(dir(item)) - _readonly_attrs

    for sitem_name in attr_names:

        sitem = getattr(item, sitem_name)
        new_sitem = getattr(new_item, sitem_name)

        try:
            # Set the item
            setattr(item, sitem_name, new_sitem)

            try:
                # Will work for classes and functions defined in that module.
                mod_name = sitem.__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(sitem) in reset_items) \
                    or isinstance(sitem, EnumMeta):  # Deal with enums
                continue

            reset_items.add(id(sitem))
            _reset_item_recursively(sitem, new_sitem, module_name, reset_items)
        except Exception as ex:
            raise Exception(sitem_name) from ex
Traceback (most recent call last): File "FFFF", line 1, in NameError: name 'YYYY' is not defined Traceback (most recent call last): File "FFFF", line 1, in File "/usr/local/lib/python3.7/importlib/__init__.py", line 140, in reload raise TypeError("reload() argument must be a module") TypeError: reload() argument must be a module
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
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()