如何从模块的项目结构外部导入 python 模块而不出现 ModuleNotFoundError?

How can I import python modules from outside the module's project structure without getting a ModuleNotFoundError?

我创建了一个 python 项目,使用 Pycharm 如果重要的话,它看起来像这样:

outer_dir/
    outside_main.py
    module_example/
        module_main.py
        tests/
            test_1.py
            …
        level_1/
            foo_1.py
            level_2/
                foo_2.py

main.pyfoo_1.py 调用函数 foo_1.pyfoo_2.py

调用函数

我试图模拟的一般用例是一个名为 module_example 的 python“包”或 repo,已被 outside_main.py 下载并使用,级别与顶级包目录。

这是 module_main.py 的样子:

# module_main.py

from level_1.foo_1 import foo_function_1
from level_1.level_2.foo_2 import foo_function_2


def main():
    print(foo_1())
    print(foo_2())


if __name__ == "__main__":
    main()

我可以从 test_1.py 调用这个顶级脚本,就像这样:

# test_1.py

from module_main import main

if __name__ == "__main__":
    main()

但是,当我尝试从 module_example 目录外部使用 main() 时,代码如下:

# outside_main.py

from module_example.module_main import main

if __name__ == "__main__":
    main()

我收到这个错误:

Traceback (most recent call last):
  File "/path/to/outside_main.py", line 1, in <module>
    from module_example.module_main import main
  File "/path/to/module_example/module_main.py", line 1, in <module>
    from level_1.foo_1 import foo_function_1
ModuleNotFoundError: No module named 'level_1'

似乎正在发生的事情是,无论导入的 python 脚本的目录如何,都会相对于执行主脚本的目录搜索它自己的导入。

我正在寻找一种导入方法,无论我是调用 outside_main.py 还是 test_1.py,也无论终端当前指向哪个目录,该方法都同样有效。

我读过 this post 关于 python 导入如何工作的内容,它基本上表明我运气不好,但这对我来说毫无意义,我正在努力做正在使用来自 github 的某人的开源库进行模拟,他们通常在他们的回购协议中有一个 tests 文件夹,工作正常,但我也可以在我的项目中解压缩整个回购协议并在他们的 py 上调用导入文件没有问题。他们是怎么做到的?

解决方案 1:

使用 explicit relative imports (relative to the files inside the directory module_example). Note that the usage of implicit relative imports 已在 Python3

中删除

Implicit relative imports should never be used and have been removed in Python 3.

outer_dir/module_example/module_main.py

# Note the "." in the beginning signifying it is located in the current folder (use ".." to go to parent folder)
from .level_1.foo_1 import foo_function_1
from .level_1.level_2.foo_2 import foo_function_2

# All the rest the same

解决方案 2:

在您的 PYTHONPATH 环境变量中包含 module_example 的路径(或者您也可以 choose 直接更新 sys.path

export PYTHONPATH=$(pwd):$(pwd)/module_example

输出:

以上两种解决方案对我来说都是成功的。

修复前:

$ python3 outside_main.py 
Traceback (most recent call last):
  File "outside_main.py", line 1, in <module>
    from module_example.module_main import main
  File "/home/nponcian/Documents/PearlPay/Program/2021_07Jul_31_Whosebug/1/module_example/module_main.py", line 1, in <module>
    from level_1.foo_1 import foo_function_1
ModuleNotFoundError: No module named 'level_1'

修复后:

$ python3 outside_main.py 
This is foo_function_1
This is foo_function_2

在进入 sys.path 补丁的地狱之前,我建议将您当前位于 module_example 中的代码包装到 package_example 目录中,添加一个 setup.py并在

中抛出一些 __init__.py

制作 package_example 一个 git 仓库。

在您要使用 module_example.[...] 的每个其他项目中,您创建一个 venvpip -e /path/to/package_example

outer_dir/
    outside_main.py
    package_example/
        .git/
            ...
        setup.py
        module_example/
            __init__.py
            module_main.py
            tests/
                ...

其中__init__.py(每个包含要导入的模块的子目录中的一个)只能是空文件并且setup.py必须根据distutils documentation填充。