Python 包引用问题

首先给定一个文件结构,这个结构在大部分的项目中都是非常常见的:

|____ run.py
|____ __init__.py
|____ module_b
| |____ __init__.py
| |____ b.py
| |____ c.py
|____ module_a
| |____ __init__.py
| |____ a.py

其中 module_amodule_bmodule_c 是三个模块,run.py 是一个总的运行脚本。

b.py 文件中实现了一个简单的打印函数:

def print_hello():
    print('hello')

执行子文件夹的 py 文件

引用同级文件

c.py 中引用 b.py 的内容:

from b import print_hello

def print_hello_from_b():
    print_hello()

if __name__ == '__main__':
    print_hello_from_b()

执行 python module_b/c.py 可以正常运行。

但是这样存在一个问题,就是如果我们的主程序 run.py 去导入这个模块运行:

from module_b import c

c.print_hello_from_b()

会遇到错误 "ModuleNotFoundError: No module named 'b'"

因为在我们当前的文件夹下是没有 b.py 文件的。而为什么 python module_b/c.py 可以正常运行是因为这个方式等价于:

cd module_b
python c.py

进入到 module_b 文件夹去执行 c.py,因此当前文件夹是包含 b.py 的。

为了避免这个问题,同时也符合谷歌的 Python 规范:"使用模块的全路径名来导入每个模块",我们需要修改 c.py 文件:

from module_b.b import print_hello

def print_hello_from_b():
    print_hello()

if __name__ == '__main__':
    print_hello_from_b()

此时 run.py 就可以正常运行了。但与此同时,如果直接执行 python module_b/c.py 会报错:“ModuleNotFoundError: No module named 'module_b'”。因为使用了绝对路径,该运行语句再将运行环境定位到模块内部,没有导入的包名了故报错。

解决方法是添加环境变量,在运行子文件夹 py 文件前,将项目环境添加到 PYTHONPATH

# 添加当前路径到 Python 环境
export PYTHONPATH=.:$PATH

再执行 python module_b/c.py 即可。

引用其他模块文件

在 a.py 文件中引用该函数:

from module_b import b

def print_hello_from_b():
    b.print_hello()

if __name__ == '__main__':
    print_hello_from_b()

此时如果执行 python module_a/a.py 会遇见错误:"ModuleNotFoundError: No module named 'module_b'"

可以把执行语句理解为:

cd module_a
python a.py

由于进入 module_a 模块内部了,因此无法找到 module_b 模块。

同样添加项目环境到 PYTHONPATH 后解决:

# 添加当前路径到 Python 环境
export PYTHONPATH=.:$PATH
python module_a/a.py