在Python中递归地重新加载包(及其子模块)

ide*_*n42 8 python python-3.x

在Python中,您可以按如下方式重新加载模块...

import foobar

import importlib
importlib.reload(foobar)
Run Code Online (Sandbox Code Playgroud)

这适用于.py文件,但对于Python包,它只会重新加载包而不是任何嵌套的子模块.

随着包:

  • foobar/__init__.py
  • foobar/spam.py
  • foobar/eggs.py

Python脚本:

import foobar

# assume `spam/__init__.py` is importing `.spam`
# so we dont need an explicit import.
print(foobar.spam)  # ok

import importlib
importlib.reload(foobar)
# foobar.spam WONT be reloaded.
Run Code Online (Sandbox Code Playgroud)

不建议这是一个错误,但有时重新加载包及其所有子模块是有用的.(例如,如果要在脚本运行时编辑模块).

在Python中以递归方式重新加载包有哪些好方法?

笔记:

  • 出于此问题的目的,假设最新的Python3.x

    (目前正在使用importlib)

  • 允许这可能需要对模块本身进行一些编辑.
  • 假设未使用通配符导入(from foobar import *),因为它们可能使重载逻辑复杂化.

ide*_*n42 6

这是一个递归加载包的函数。仔细检查重新加载的模块是否在使用它们的模块中更新,并检查无限递归问题。

一种重构是它需要在一个包上运行(无论如何这只对包有意义)

import os
import types
import importlib


def reload_package(package):
    assert(hasattr(package, "__package__"))
    fn = package.__file__
    fn_dir = os.path.dirname(fn) + os.sep
    module_visit = {fn}
    del fn

    def reload_recursive_ex(module):
        importlib.reload(module)

        for module_child in vars(module).values():
            if isinstance(module_child, types.ModuleType):
                fn_child = getattr(module_child, "__file__", None)
                if (fn_child is not None) and fn_child.startswith(fn_dir):
                    if fn_child not in module_visit:
                        # print("reloading:", fn_child, "from", module)
                        module_visit.add(fn_child)
                        reload_recursive_ex(module_child)

    return reload_recursive_ex(package)

# example use
import os
reload_package(os)
Run Code Online (Sandbox Code Playgroud)

  • 这个函数并不完全正确,考虑有两个子模块 A 和 B。A 中的对象依赖于 B。假设 A 首先重新加载,A 将导入旧的 B(因为 B 没有重新加载)。它导致未完全更新的 A。 (3认同)