'reload'的递归版本

Mar*_*ery 24 python python-module python-import

当我开发Python代码时,我通常在解释器中以临时方式测试它.我将import some_module测试它,找到一个bug,修复bug并保存,然后再使用内置reload函数进行reload(some_module)测试.

但是,假设在some_module我有import some_other_module,并且在测试时some_module我发现了一个错误some_other_module并修复它.现在调用reload(some_module)不会递归重新导入some_other_module.我必须手动重新导入依赖项(通过执行类似的操作reload(some_module.some_other_module),或者import some_other_module; reload(some_other_module),或者,如果我已经更改了一大堆依赖项并且丢失了我需要重新加载的内容,我需要重新启动整个解释器.

更方便的是,如果有一些recursive_reload功能,我可以做recursive_reload(some_module),让Python不仅重新加载some_module,而且还递归地重新加载每个some_module导入的模块(以及每个模块导入的每个模块,等等)我可以肯定我没有使用任何其他模块的旧版本some_module.

我不认为Python内置任何内容就像recursive_reload我在这里描述的功能一样,但有没有一种简单的方法可以将这样的东西组合在一起?

小智 30

我遇到了同样的问题,你激励我真正解决问题.

from types import ModuleType

try:
    from importlib import reload  # Python 3.4+
except ImportError:
    # Needed for Python 3.0-3.3; harmless in Python 2.7 where imp.reload is just an
    # alias for the builtin reload.
    from imp import reload

def rreload(module):
    """Recursively reload modules."""
    reload(module)
    for attribute_name in dir(module):
        attribute = getattr(module, attribute_name)
        if type(attribute) is ModuleType:
            rreload(attribute)
Run Code Online (Sandbox Code Playgroud)

或者,如果您正在使用IPython,只需使用dreload或传递--deep-reload启动.


red*_*dsk 7

我遇到了同样的问题,并且我已经建立了@Mattew 和@osa 的答案。

from types import ModuleType
import os, sys
def rreload(module, paths=None, mdict=None):
    """Recursively reload modules."""
    if paths is None:
        paths = ['']
    if mdict is None:
        mdict = {}
    if module not in mdict:
        # modules reloaded from this module
        mdict[module] = [] 
    reload(module)
    for attribute_name in dir(module):
        attribute = getattr(module, attribute_name)
        if type(attribute) is ModuleType:
            if attribute not in mdict[module]:
                if attribute.__name__ not in sys.builtin_module_names:
                    if os.path.dirname(attribute.__file__) in paths:
                        mdict[module].append(attribute)
                        rreload(attribute, paths, mdict)
    reload(module)
    #return mdict
Run Code Online (Sandbox Code Playgroud)

有以下三个区别:

  1. 在一般情况下,正如 @osa 指出的那样,还必须在函数末尾调用 reload(module) 。
  2. 使用循环导入依赖项,之前发布的代码将永远循环,因此我添加了一个列表字典来跟踪其​​他模块加载的模块集。虽然循环依赖并不酷,但 Python 允许它们,因此这个重新加载函数也可以处理它们。
  3. 我添加了允许重新加载的路径列表(默认为 [''])。有些模块不喜欢以正常方式重新加载(如此处所示


Nli*_*tis 5

实际编写一些测试用例并在每次完成模块修改后都运行它们会更简单吗?

您正在做的事情很酷(本质上是在使用TDD(测试驱动的开发),但是您做错了。

考虑到与写单元测试(使用默认的Python 单元测试模块,或者更好的鼻子)你有那些测试可重复使用的稳定的,并帮助您检测inconsitencies在你的代码要更快,更好比在交互式测试你的模块环境。

  • 换句话说,单元测试是确认代码是否有效的好工具-或在您不希望它们存在的地方检测错误的方法,而无需花费太多的人力。它们不是用来摆弄您当前正在从头开始构建的代码的好工具,这些代码已经完成或已经损坏,在以后的情况下,我使用交互式解释器。 (9认同)
  • 具有讽刺意味的是:当我遇到这个问题之后,当我在测试一些我正在为另一个应用程序编写的单元测试时,被激发出问这个问题。如果要在这里应用您的建议,我最终将无限递归并创建无止尽的测试层。;) (4认同)
  • 更认真的说:适当的测试当然有其位置,但是有时需要一种更随意的方法(即使它是对适当测试的补充,而不是代替适当的测试),而Python的优点之一就是它使这种随意的方法成为可能。简单。也许我已经编写了一半的函数,并且想要检查到目前为止它是否可以输出我期望的结果,或者也许我只是将一大堆打印语句插入了一些棘手的代码中,并且想要在一些不正确的参数上运行该函数,我指定并查看打印出来的内容。单元测试不适用于这些情况。 (3认同)