使用“当前目录”导入导入模块

Pas*_*ten 3 python import python-importlib python-3.5

我有以下代码来动态加载模块:

def load_module(absolute_path):
    import importlib.util
    module_name, _ = os.path.splitext(os.path.split(absolute_path)[-1])
    try:
        py_mod = imp.load_source(module_name, absolute_path)
    except ImportError:
        module_root = os.path.dirname(absolute_path)
        print("Could not directly load module, including dir: {}".format(module_root))
        spec = importlib.util.spec_from_file_location(
            module_name, absolute_path, submodule_search_locations=[module_root, "."])
        py_mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(py_mod)
    return py_mod
Run Code Online (Sandbox Code Playgroud)

它工作得非常好,除非它尝试在同一文件夹中导入脚本(而不是同名包的一部分)。例如,脚本a.py正在做import b. 它会导致错误ImportError: No module named 'b'(这在 Python 3 中很常见)。

但我真的很想找到解决这个问题的方法吗?可以通过添加以下内容来解决:

import sys
sys.path.append(".")
Run Code Online (Sandbox Code Playgroud)

编写“a”脚本。

虽然我希望它可以通过以下方式解决:

submodule_search_locations=[module_root, "."]
Run Code Online (Sandbox Code Playgroud)

哦,是的,理由是我还想支持导入不是正确包/模块的模块,而只是一些可以在解释器中工作的脚本。

可重现的代码

〜/示例/ a.py

import b
Run Code Online (Sandbox Code Playgroud)

〜/示例/ b.py

print("hi")
Run Code Online (Sandbox Code Playgroud)

~/somewhere_else/main.py(与 a/b 不同)

import sys, os, imp, importlib.util

def load_module(absolute_path) ...

load_module(sys.argv[1])
Run Code Online (Sandbox Code Playgroud)

然后在命令行上运行:

cd ~/somewhere_else
python3.5 main.py /home/me/example/a.py
Run Code Online (Sandbox Code Playgroud)

这导致 ImportError: No module named 'b'

下面的代码解决了它,但当然我们不能sys.path在所有脚本中手动放置东西。

~/example/a.py (2)

import sys
sys.path.append(".")
import b
Run Code Online (Sandbox Code Playgroud)

我真的希望其他人可能有一个我还没有想到的解决方案。

附录

def load_module(absolute_path):
    import importlib.util
    module_name, _ = os.path.splitext(os.path.split(absolute_path)[-1])
    try:
        py_mod = imp.load_source(module_name, absolute_path)
    except ImportError as e:
        if "No module named" not in e.msg:
            raise e

        missing_module = e.name
        module_root = os.path.dirname(absolute_path)

        if missing_module + ".py" not in os.listdir(module_root):
            msg = "Could not find '{}' in '{}'"
            raise ImportError(msg.format(missing_module, module_root))

        print("Could not directly load module, including dir: {}".format(module_root))
        sys.path.append(module_root)
        spec = importlib.util.spec_from_file_location(module_name, absolute_path)
        py_mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(py_mod)
    return py_mod
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 5

在这里使用动态导入并不重要;同样的问题适用于任何假设当前目录在路径上的代码。这些脚本有责任确保当前目录位于路径上。

而不是使用'.'(当前工作目录),使用__file__全局将目录添加到路径:

import os.path
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
Run Code Online (Sandbox Code Playgroud)

可以通过添加重试您的动态导入os.path.dirname(absolute_path)sys.path当你有一个ImportError(也许检测,这是一个短暂的导入失败),但是这是一个巨大的飞跃,使你不能缺少的依赖,并作出有关的假设一个模块之间的区别sys.path