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

发布于 2024-07-12 05:19:22 字数 195 浏览 7 评论 0 原文

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

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

I have a long-running Python server and would like to be able to upgrade a service without restarting the server. What's the best way do do this?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

清风无影 2024-07-19 05:19:22

您可以使用 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 模块的代码并重新执行模块级代码,通过重用 loader 最初加载模块。 扩展模块的init函数不会被第二次调用。
  • 与 Python 中的所有其他对象一样,旧对象仅在其引用计数降至零后才会被回收。
  • 模块命名空间中的名称会更新以指向任何新的或更改的对象。
  • 对旧对象的其他引用(例如模块外部的名称)不会重新绑定以引用新对象,并且如果需要,必须在它们出现的每个命名空间中进行更新。

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

You can reload a module when it has already been imported by using importlib.reload():

from importlib import reload  # Python 3.4+
import foo

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

In Python 2, reload was a builtin. In Python 3, it was moved to the imp module. In 3.4, imp was deprecated in favor of importlib. When targeting 3 or later, either reference the appropriate module when calling reload or import it.

I think that this is what you want. Web servers like Django's development server use this so that you can see the effects of your code changes without restarting the server process itself.

To quote from the docs:

  • Python module’s code is recompiled and the module-level code re-executed, defining a new set of objects which are bound to names in the module’s dictionary by reusing the loader which originally loaded the module. 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.

As you noted in your question, you'll have to reconstruct Foo objects if the Foo class resides in the foo module.

末が日狂欢 2024-07-19 05:19:22

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

BDFL回答这个问题。

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

因此,我认为您现在应该使用 importlib.reload(module),虽然我不确定。

In Python 3.0–3.3 you would use: imp.reload(module)

The BDFL has answered this question.

However, imp was deprecated in 3.4, in favour of importlib (thanks @Stefan!).

I think, therefore, you’d now use importlib.reload(module), although I’m not sure.

聊慰 2024-07-19 05:19:22

如果模块不是纯 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() 函数的函数。

It can be especially difficult to delete a module if it is not pure Python.

Here is some information from: How do I really delete an imported module?

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

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

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

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

桃扇骨 2024-07-19 05:19:22

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

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

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

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

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

reload(module), but only if it's completely stand-alone. If anything else has a reference to the module (or any object belonging to the module), then you'll get subtle and curious errors caused by the old code hanging around longer than you expected, and things like isinstance not working across different versions of the same code.

If you have one-way dependencies, you must also reload all modules that depend on the reloaded module to get rid of all the references to the old code. And then reload modules that depend on the reloaded modules, recursively.

If you have circular dependencies, which is very common for example when you are dealing with reloading a package, you must unload all the modules in the group in one go. You can't do this with reload() because it will re-import each module before its dependencies have been refreshed, allowing old references to creep into new modules.

The only way to do it in this case is to hack sys.modules, which is kind of unsupported. You'd have to go through and delete each sys.modules entry you wanted to be reloaded on next import, and also delete entries whose values are None to deal with an implementation issue to do with caching failed relative imports. It's not terribly nice but as long as you have a fully self-contained set of dependencies that doesn't leave references outside its codebase, it's workable.

It's probably best to restart the server. :-)

甜心小果奶 2024-07-19 05:19:22

对于 Python 2 使用内置函数 reload

reload(module)

对于 Python 2 Python 3.23.3 使用 从模块 imp 重新加载

import imp
imp.reload(module)

对于 Python ≥3.4imp 已弃用支持 importlib,所以使用这个:

import importlib
importlib.reload(module)

或:

from importlib import reload
reload(module)

TL;DR:

Python ≥ 3.4:importlib.reload(module)
Python 3.2 — 3.3:imp.reload(module)
Python 2:重新加载(模块)

For Python 2 use built-in function reload:

reload(module)

For Python 2 and Python 3.23.3 use reload from module imp:

import imp
imp.reload(module)

For Python ≥3.4, imp is deprecated in favor of importlib, so use this:

import importlib
importlib.reload(module)

or:

from importlib import reload
reload(module)

TL;DR:

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

黑白记忆 2024-07-19 05:19:22
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]
以为你会在 2024-07-19 05:19:22

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

首先,确保您使用的是来自 Jupyter Notebook 项目的优秀 IPython shell。 安装 Jupyter 后,您可以使用 ipython 或 jupyter console 启动它,甚至更好的是 jupyter qtconsole ,这将为您提供漂亮的彩色效果在任何操作系统中具有代码完成功能的控制台。

现在,在 shell 中输入:

%load_ext autoreload
%autoreload 2

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

除了2之外,还有其他选项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.

当然,它也适用于 Jupyter Notebook。

If you are not in a server, but developing and need to frequently reload a module, here's a nice tip.

First, make sure you are using the excellent IPython shell, from the Jupyter Notebook project. After installing Jupyter, you can start it with ipython, or jupyter console, or even better, jupyter qtconsole, which will give you a nice colorized console with code completion in any OS.

Now in your shell, type:

%load_ext autoreload
%autoreload 2

Now, every time you run your script, your modules will be reloaded.

Beyond the 2, there are other 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.

Sure, it will also work on a Jupyter Notebook.

相对绾红妆 2024-07-19 05:19:22

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

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

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

The following code allows you Python 2/3 compatibility:

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

The you can use it as reload() in both versions which makes things simpler.

你的他你的她 2024-07-19 05:19:22

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

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

The accepted answer doesn't handle the from X import Y case. This code handles it and the standard import case as well:

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

In the reloading case, we reassign the top level names to the values stored in the newly reloaded module, which updates them.

眼眸里的那抹悲凉 2024-07-19 05:19:22

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

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 模块。

This is the modern way of reloading a module:

from importlib import reload

If you want to support versions of Python older than 3.5, use this:

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+

This defines a reload method which can be called with a module to reload it. For example, reload(math) will reload the math module.

寂寞花火° 2024-07-19 05:19:22

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

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

更多信息请参见重新加载 Python 模块

For those like me who want to unload all modules (when running in the Python interpreter under Emacs):

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

More information is in Reloading Python modules.

星光不落少年眉 2024-07-19 05:19:22

编辑(答案 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 关键字导入的,例如 import numpy as np),该实例不会被覆盖。

当应用需要配置模块“全新”状态的测试时,这对我来说是一个很大的问题,因此我编写了一个名为 reset_module 的函数,它使用 importlib' s reload 函数并递归地覆盖所有声明的模块的属性。 它已经使用 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 中的内部问题(例如 pickling/un-pickling 问题)。

Edit (Answer V2)

The solution from before is good for just getting the reset information, but it will not change all the references (more than reload but less then required). To actually set all the references as well, I had to go into the garbage collector, and rewrite the references there. Now it works like a charm!

Note that this will not work if the GC is turned off, or if reloading data that's not monitored by the GC. If you don't want to mess with the GC, the original answer might be enough for you.

New code:

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)

Original Answer

As written in @bobince's answer, if there's already a reference to that module in another module (especially if it was imported with the as keyword like import numpy as np), that instance will not be overwritten.

This proved quite problematic to me when applying tests that required a "clean-slate" state of the configuration modules, so I've written a function named reset_module that uses importlib's reload function and recursively overwrites all the declared module's attributes. It has been tested with Python version 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

Note: Use with care! Using these on non-peripheral modules (modules that define externally-used classes, for example) might lead to internal problems in Python (such as pickling/un-pickling issues).

满栀 2024-07-19 05:19:22

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

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

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

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

pip3 install jupyter

Other option. See that Python default importlib.reload will just reimport the library passed as an argument. It won't reload the libraries that your lib import. If you changed a lot of files and have a somewhat complex package to import, you must do a deep reload.

If you have IPython or Jupyter installed, you can use a function to deep reload all libs:

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

If you don't have Jupyter, install it with this command in your shell:

pip3 install jupyter
蓝咒 2024-07-19 05:19:22

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

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

Those who are using python 3 and reload from importlib.

If you have problems like it seems that module doesn't reload... That is because it needs some time to recompile pyc (up to 60 sec).I writing this hint just that you know if you have experienced this kind of problem.

陪你到最终 2024-07-19 05:19:22

Enthought Traits 有一个模块可以很好地解决这个问题。 https://traits.readthedocs.org/en/ 4.3.0/_modules/traits/util/refresh.html

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

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

Enthought Traits has a module that works fairly well for this. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html

It will reload any module that has been changed, and update other modules and instanced objects that are using it. It does not work most of the time with __very_private__ methods, and can choke on class inheritance, but it saves me crazy amounts of time from having to restart the host application when writing PyQt guis, or stuff that runs inside programs such as Maya or Nuke. It doesn't work maybe 20-30 % of the time, but it's still incredibly helpful.

Enthought's package doesn't reload files the moment they change - you have to call it explicitely - but that shouldn't be all that hard to implement if you really need it

听,心雨的声音 2024-07-19 05:19:22

2018-02-01

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

31.5。 importlib — import 的实现 — Python 3.6.4 文档

2018-02-01

  1. module foo must be imported successfully in advance.
  2. from importlib import reload, reload(foo)

31.5. importlib — The implementation of import — Python 3.6.4 documentation

淑女气质 2024-07-19 05:19:22

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

回溯(最近一次调用最后一次):  
   文件“FFFF”,第 1 行,位于   
  NameError:名称“YYYY”未定义

或者

回溯(最近一次调用最后一次): 
   文件“FFFF”,第 1 行,位于  
   文件“/usr/local/lib/python3.7/importlib/__init__.py”,第 140 行,重新加载 
     raise TypeError("reload() 参数必须是一个模块") 
  类型错误: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 code> 仅接受模块而不接受字符串。

If you encounter the following error, this answer may help you to get a solution:

Traceback (most recent call last): 
 File "FFFF", line 1, in  
NameError: name 'YYYY' is not defined

OR

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

In case you have an import like the one below, you may need to use the sys.modules to get the module you want to reload:

  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

The main issue is that the importlib.reload accepts module only not string.

妄司 2024-07-19 05:19:22

对我来说,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 me for case of Abaqus it is the way it works.
Imagine your file is 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'])
浊酒尽余欢 2024-07-19 05:19:22

重新加载时,Python不会重新计算子模块地址,如果它位于sys.modules中,

这是一个解决方法,并不完美,但有效。

# Created by [email protected] 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

Python will not re-calculate submodule address when reload, event if it is in sys.modules

Here is a workaround, not perfect but worked.

# Created by [email protected] 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
戈亓 2024-07-19 05:19:22

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

Removing modules from sys.modules requires 'None' types to be deleted as well.

Method 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

Method 2, using bookkeeping entries, to remove all dependencies:

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

Optional, just to be certain all entries are out, if you so choose:

import gc
gc.collect()
謌踐踏愛綪 2024-07-19 05:19:22

我在尝试在 Sublime Text 中重新加载某些内容时遇到了很多麻烦,但最终我可以编写这个实用程序,根据 sublime_plugin.py 用于重新加载模块的代码在 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()

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

I got a lot of trouble trying to reload something inside Sublime Text, but finally I could wrote this utility to reload modules on Sublime Text based on the code sublime_plugin.py uses to reload modules.

This below accepts you to reload modules from paths with spaces on their names, then later after reloading you can just import as you usually do.

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

If you run for the first time, this should load the module, but if later you can again the method/function run_tests() it will reload the tests files. With Sublime Text (Python 3.3.6) this happens a lot because its interpreter never closes (unless you restart Sublime Text, i.e., the Python3.3 interpreter).

冰火雁神 2024-07-19 05:19:22

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

Another way could be to import the module in a function. This way when the function completes the module gets garbage collected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文