ChatGPT解决这个技术问题 Extra ChatGPT

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

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

if foo.py has changed:
    unimport foo  <-- How do I do this?
    import foo
    myfoo = foo.Foo()
备忘提示:“import”并不意味着“load”,它的意思是“如果尚未加载则加载然后导入命名空间”。
这个问题不应该包括“卸载”,因为这在 python 中是不可能的 - 然而,重新加载是一个已知的范例,如下所示
在 py2exe 应用程序中使用动态模块时我遇到了同样的问题。由于 py2exe 始终将字节码保留在 zip 目录中,因此重新加载不起作用。但我找到了一个使用 import_file 模块的工作解决方案。现在我的应用程序运行良好。
如果您想“卸载”,因为代码正在使用试图删除 .pyc 文件怎么办?

9
9 revs, 8 users 53%

您可以使用 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 中,它是 movedimp 模块。在 3.4 中,impdeprecated 支持 importlib。定位 3 或更高版本时,在调用 reload 时引用相应的模块或导入它。

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

引用文档:

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

正如您在问题中所指出的,如果 Foo 类驻留在 foo 模块中,您将不得不重构 Foo 对象。


实际上,当您更改文件时,django dev 服务器会自行重新启动..(它会重新启动服务器,而不仅仅是重新加载模块)
这个“is_changed”函数来自哪里?我没有看到关于它的文档,它不在我的 Python 3.1.3 环境中运行,也没有在 2.6.4 中运行。
没有 cdleary,Django 不能只使用重新加载:pyunit.sourceforge.net/notes/reloading.html
对于具有依赖关系的模块,重新加载是不够的。请参见下面的 bobince:stackoverflow.com/a/438845/456878。这曾经咬过我,浪费了 10 分钟。
@jedmao @JamesDraper 我很确定 is_changed 函数只是您必须编写的任意函数;它不是内置的。例如,它可能会打开与您正在导入的模块相对应的文件,并将其与缓存版本进行比较,以查看它是否已更改。
C
Community

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

BDFLanswered这个问题。

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

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


认真的新手很高兴了解 Python 2 和 3 之间的关键细微差别。
@LoïcFaure-Lacroix reload(__builtins__) 在 2.x 中同样有效
@Tarrasch:这是您要重新加载的 Python 模块,就像问题中的示例一样。
@LoïcFaure-Lacroix 是的,imp 可以自行重新加载。
@PaulD.Waite,可以确认这在 Python 3.6.5 中有效
H
Honest Abe

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


我刚刚发现,如果模块是包的一部分,您也必须在那里删除它:setattr(package, "empty", None)
这是正确的解决方案,特别是如果您有一个包含嵌套模块的包。 reload() 仅重新加载最顶层的模块,除非您先从 sys.modules 中删除它,否则不会重新加载其中的任何内容。
H
HoldOffHunger

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

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

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

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

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


dreload 不是专门针对这种情况的吗?
@Josh:不,它用于重新加载包树,即使这样,只要包没有外部/循环依赖项,它也只能工作。
您能否详细说明具有 None 值的部分,因为我正好遇到了这个问题:我正在从 sys.modules 中删除项目,并且在重新导入后,一些导入的依赖项是 None
@Eliethesaiyan:您的意思是 reload 函数吗?它是内置的,您不必导入任何库。
我在下面编写了一个函数,它确实用新模块的内容覆盖了前一个模块的内容,因此处理了@bobince 正确提到的问题。请参阅stackoverflow.com/a/61617169/2642356
g
goetzc

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

reload(module)

对于 Python 2 Python 3.23.3 使用 reload from module imp

import imp
imp.reload(module)

对于 Python ≥3.4imp is deprecated 支持 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)


处理以下任何一种情况:from six import reload_module(当然首先需要 pip install six
@Anentropic:建议使用六个包,但语法是 from six.moves import reload_module (doc)
D
Daniel
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]

+1。我的目标是在 python 中运行鼻子测试。在我加载了一个模块并重命名了一些函数后,调用 nose.run() 时旧名称仍然存在,即使在 reload(my_module) %run my_module 之后
如果您的模块导入它自己的子模块,您可能也需要删除这些子模块。 [del(sys.modules[mod] for mod in sys.modules.keys() if mod.startswith('myModule.')] 之类的东西。
我不认为这会卸载模块。在 Python 3.8 上:import sys; import json; del sys.modules['json']; print(json.dumps([1])) 和 json 模块仍在工作,即使它不再位于 sys.modules 中。
是的,我注意到一个带下划线的 sys.modules['_json'] 条目,考虑到这一点后它仍然会打印出来 for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') ]: del sys.modules[mod]
是的。即使使用更积极的簿记,删除条目及其所有依赖项也不会删除模块的功能。 before = [mod for mod in sys.modules] ; import json ; after = [mod for mod in sys.modules if mod not in before] ; for mod in [ m for m in sys.modules if m in after ]: del sys.modules[mod](代码块没有保留换行符。;表示换行符)
M
Matt Clarkson

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

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

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


J
Joseph Garvin

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

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


注意到一个问题, globals() 指的是您在其中定义此函数的模块,因此如果您在与您在其中调用它的模块不同的模块中定义它,则它不起作用。
对于交互式,在 >>> from X import Y 之后重新加载执行 >>> __import__('X', fromlist='Y')
@BobStein-VisiBone,有没有办法在 fromlist='*' 时让它工作?
好问题,不知道@MikeC。顺便说一句,我倾向于停止在 import 语句中几乎所有使用 from 。只需在代码中明确 import <package> 和显式 package.symbol 即可。意识到这可能并不总是可行或可取的。 (这里有一个例外:从未来导入 print_function。)
Mike C:对我有用的是foo = reload(foo); from foo import *
n
neves

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

首先,确保您使用的是 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.

当然,它也适用于 Jupyter Notebook。


R
Richie Bendall

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

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


或者直接执行 from importlib import reload。然后你可以做reload(MODULE_NAME)。不需要此功能。
我相信 modulereload(MODULE_NAME)reload(MODULE_NAME) 更易于解释,并且与其他函数发生冲突的可能性更低。
@RichieBendall 对不起,但这个答案是完全错误的。 reload() 函数采用模块对象,而不是模块名称...阅读文档:docs.python.org/3/library/importlib.html#importlib.reload 我同意@pault - 这个“作为 modulereload”是多余的。
我已经更改了答案以反映您的意见。
P
Peter Mortensen

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

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

Reloading Python modules 中有更多信息。


实际上,这似乎并不可靠(在 2.6 中),因为并非 sys.modules.values() 中的所有内容都是模块。例如:>>> type(sys.modules.values()[1]) <class 'email.LazyImporter'>因此,如果我尝试运行该代码,它就会失败(我知道它并不是一个实用的解决方案,只是指出这一点)。
它甚至在早期的 python 中都不起作用 - 正如所写的那样。我不得不排除一些名字。当我将该代码移动到我的新计算机时,我会更新帖子。
经过一些修改后在 Python 2.7 中运行良好:if mod and mod.__name__ != "__main__": imp.reload(mod)
这对我很有效: import imp [reload(m) for m in sys.modules.values() if m and not "" in m.__name 而不是 imp.is_builtin(m.__name__)]
D
David Ghandehari

编辑(答案 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 的回答中所写,如果在另一个模块中已经引用了该模块(特别是如果它是使用 import numpy as npas 关键字导入的),则该实例不会被覆盖。

在应用需要配置模块的“清白”状态的测试时,这对我来说非常有问题,因此我编写了一个名为 reset_module 的函数,它使用 importlibreload 函数并递归地覆盖所有声明模块的属性。它已经用 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 中的内部问题(例如酸洗/非酸洗问题)。


f
flipthefrog

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

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

Enthought 的包不会在文件更改的那一刻重新加载文件——你必须明确地调用它——但如果你真的需要它,这应该不难实现


I
IanS

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

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

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

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

pip3 install jupyter

importlib 中的这个 Ipython dreload 和 reload() 都抱怨 reload() argument must be module。我正在使用自定义函数导入,但似乎不起作用。使用内置模块确实有效。 :-( 对于我对代码所做的每一个小改动都重新加载 iPython 是浪费时间......
P
PythonMan

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

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


A
AbstProcDo

2018-02-01

模块 foo 必须提前成功导入。从 importlib 导入重新加载,重新加载(foo)

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


M
Matt S

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

此答案是从此处直接复制的:ebanshi.cc/questions/1942/…
m
minus one

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

Traceback(最近一次调用最后一次):文件“FFFF”,第 1 行,在 NameError:名称 'YYYY' 未定义

或者

回溯(最后一次调用):文件“FFFF”,第 1 行,在文件“/usr/local/lib/python3.7/importlib/__init__.py”中,第 140 行,在重新加载中引发 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 只接受模块而不接受字符串。


D
Doyousketch2

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

B
BaiJiFeiLong

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

h
hackbot89

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


该模块永远不会被垃圾回收,因为至少在 sys.modules 中保存了一个全局引用。
u
user

我在尝试在 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 解释器)。