974

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

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

22 回答 22

990

您可以使用以下命令重新加载已经导入的模块importlib.reload()

from importlib import reload  # Python 3.4+
import foo

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

在 Python 2 中,reload是内置的。在 Python 3 中,它被移到imp模块中。在 3.4 中,imp弃用,取而代之的是importlib. 以 3 或更高版本为目标时,在调用reload或导入时引用适当的模块。

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

引用文档:

  • 重新编译 Python 模块的代码并重新执行模块级代码,通过重用最初加载模块的加载器,定义一组新的对象,这些对象绑定到模块字典中的名称。init扩展模块的功能不会被第二次调用。
  • 与 Python 中的所有其他对象一样,旧对象仅在其引用计数降至零后才会被回收。
  • 模块命名空间中的名称被更新以指向任何新的或更改的对象。
  • 对旧对象的其他引用(例如模块外部的名称)不会重新绑定以引用新对象,并且如果需要,必须在它们出现的每个命名空间中进行更新。

正如您在问题中指出的那样,如果该类驻留在模块中,则必须重建Foo对象。Foofoo

于 2009-01-13T00:34:40.110 回答
265

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

BDFL已经回答这个问题。

但是,imp在 3.4 中已弃用,取而代之的是importlib(感谢@Stefan!)。

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

于 2009-10-10T13:36:30.443 回答
118

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

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

您可以使用 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() 函数的工件。

于 2009-01-28T14:03:47.030 回答
88

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

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

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

在这种情况下,唯一的方法是 hack sys.modules,这是不受支持的。您必须检查并删除sys.modules您希望在下次导入时重新加载的每个条目,并删除其值None用于处理与缓存失败的相对导入有关的实现问题的条目。这不是非常好,但只要你有一组完全独立的依赖项,不会在其代码库之外留下引用,它是可行的。

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

于 2009-01-13T12:53:25.780 回答
76

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

reload(module)

对于 Python 2 Python 3.2 —<strong>3.3 使用reload模块 imp

import imp
imp.reload(module)

对于 Python ≥<strong>3.4,imp 不推荐使用importlib所以使用这个:

import importlib
importlib.reload(module)

或者:

from importlib import reload
reload(module)

TL;博士:

Python ≥ 3.4:importlib.reload(module)
Python 3.2 — 3.3: imp.reload(module)
Python 2:reload(module)

于 2015-08-09T17:28:31.073 回答
68
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]
于 2010-07-07T11:44:10.433 回答
26

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

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

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

于 2012-11-20T16:01:08.647 回答
23

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

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

于 2014-09-27T23:30:59.510 回答
23

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

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

现在在你的 shell 中,输入:

%load_ext autoreload
%autoreload 2

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

除了 之外2,还有autoreload 魔法的其他选项

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

当然,它也适用于 Jupyter Notebook。

于 2018-03-18T13:46:54.897 回答
20

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

from importlib import reload

如果要支持 3.5 之前的 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可以被模块调用以重新加载它的方法。例如,reload(math)将重新加载math模块。

于 2017-11-21T18:04:00.663 回答
7

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

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

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

于 2010-05-08T16:50:12.877 回答
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 的包不会在文件更改的那一刻重新加载文件——你必须明确地调用它——但如果你真的需要它,这应该不难实现

于 2016-01-09T01:06:14.893 回答
5

其他选项。看到 Python 默认importlib.reload只会重新导入作为参数传递的库。它不会重新加载您的 lib 导入的库。如果您更改了很多文件并且要导入一个有些复杂的包,则必须进行深度重新加载

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

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

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

pip3 install jupyter
于 2018-05-08T18:07:40.667 回答
5

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

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

于 2018-05-17T11:00:43.527 回答
4

2018-02-01

  1. 模块foo必须提前成功导入。
  2. from importlib import reload,reload(foo)

31.5。importlib — 导入的实现 — Python 3.6.4 文档

于 2018-02-01T03:44:57.640 回答
4

编辑(答案 V2)

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

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

新代码:

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)

原始答案

正如@bobince 的回答中所写,如果另一个模块中已经有对该模块的引用(特别是如果它是使用as关键字 like导入的import numpy as np),则该实例不会被覆盖。

在应用需要配置模块的“干净”状态的测试时,这对我来说是一个很大的问题,所以我编写了一个名为的函数reset_module,它使用importlib'sreload函数并递归地覆盖所有声明的模块的属性。它已经用 Python 3.6 版进行了测试。

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

注意:小心使用!在非外围模块(例如,定义外部使用的类的模块)上使用这些可能会导致 Python 中的内部问题(例如酸洗/非酸洗问题)。

于 2020-05-05T15:44:30.233 回答
2

对我来说,就 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'])
于 2016-02-05T14:27:12.423 回答
2

如果您遇到以下错误,此答案可能会帮助您获得解决方案:

回溯(最近一次通话最后):
 文件“FFFF”,第 1 行,在  
NameError: 名称 'YYYY' 未定义

或者

回溯(最近一次通话最后):
 文件“FFFF”,第 1 行,在
 文件“/usr/local/lib/python3.7/importlib/__init__.py”,第 140 行,重新加载
   raise TypeError("reload() 参数必须是一个模块")
TypeError: reload() 参数必须是一个模块

如果您有如下所示的导入,您可能需要使用sys.modules来获取要重新加载的模块:

  import importlib
  import sys

  from YYYY.XXX.ZZZ import CCCC
  import AAA.BBB.CC


  def reload(full_name)
    if full_name in sys.modules:
      importlib.reload(sys.modules[full_name])


  reload('YYYY.XXX.ZZZ') # this is fine in both cases
  reload('AAA.BBB.CC')  

  importlib.reload(YYYY.XXX.ZZZ) # in my case: this fails
  importlib.reload(AAA.BBB.CC)   #             and this is ok

主要问题是importlib.reload接受模块不是字符串。

于 2020-07-24T12:03:30.473 回答
1

从 sys.modules 中删除模块也需要删除“无”类型。

方法一:

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()
于 2020-10-15T07:31:21.707 回答
1

Python不会重新计算子模块地址reload,如果在,则事件在sys.modules

这是一种解决方法,虽然不完美但有效。

# Created by BaiJiFeiLong@gmail.com at 2022/2/19 18:50
import importlib
import types

import urllib.parse
import urllib.request


def reloadModuleWithChildren(mod):
    mod = importlib.reload(mod)
    for k, v in mod.__dict__.items():
        if isinstance(v, types.ModuleType):
            setattr(mod, k, importlib.import_module(v.__name__))


fakeParse = types.ModuleType("urllib.parse")
realParse = urllib.parse

urllib.parse = fakeParse
assert urllib.parse is fakeParse

importlib.reload(urllib)
assert urllib.parse is fakeParse
assert getattr(urllib, "parse") is fakeParse

reloadModuleWithChildren(urllib)
assert urllib.parse is not fakeParse
assert urllib.parse is realParse
于 2022-02-19T11:14:15.817 回答
0

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

于 2015-02-11T21:13:42.687 回答
0

尝试在 Sublime Text 中重新加载某些内容时遇到了很多麻烦,但最后我可以编写这个实用程序来根据用于重新加载模块的代码在 Sublime Text 上sublime_plugin.py重新加载模块。

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

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

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

于 2017-11-09T18:36:53.067 回答