Python 的绝对导入和相对导入

绝对导入需要包在 sys.path 中,可以通过环境变量 PYTHONPATH 来增加一些搜索路径。

相对导入需要当前在一个子包内。Relative imports in Python 3 - Stack Overflow 这个回答就说明如果直接运行一个包含了 import .xx 或者 from .xx import xx 的脚本就会失败,我的直观感受是这样的文件只能出现在比 main 文件(__name____main__ 的那个文件,也就是入口文件)更深的文件夹下。相对导入有助于避免 sys.path 中出现更靠前的搜索路径,且该路径包含同名包,导致真正要导入的包被覆盖。

例子(什么时候相对导入会出错):

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py  # from .mymodule import xx

直接运行 main.py 和 mymodule.py 都 OK,但是运行 myothermodule.py 则报错说没有 parent module。通过 python -m 来将文件视为一个模块运行,则可以将文件所在的文件夹作为 module 来运行,提供了 module 环境,但是该文件所在的文件夹并不会被加入 sys.path 中。

例子(直接运行脚本和视为模块运行的差异):

(tiny-llm-zh) ➜  tiny-llm-zh git:(main) ✗ tree test0
test0
├── __pycache__
│   └── test.cpython-310.pyc
└── test.py

2 directories, 2 files
(tiny-llm-zh) ➜  tiny-llm-zh git:(main) ✗ cat test0/test.py 
import sys
print('\n'.join(sys.path))
(tiny-llm-zh) ➜  tiny-llm-zh git:(main) ✗ python test0/test.py
/home/xx/projects/tiny-llm-zh/test0
/home/xx/micromamba/envs/tiny-llm-zh/lib/python310.zip
/home/xx/micromamba/envs/tiny-llm-zh/lib/python3.10
/home/xx/micromamba/envs/tiny-llm-zh/lib/python3.10/lib-dynload
/home/xx/micromamba/envs/tiny-llm-zh/lib/python3.10/site-packages
(tiny-llm-zh) ➜  tiny-llm-zh git:(main) ✗ python -m test0.test
/home/xx/projects/tiny-llm-zh
/home/xx/micromamba/envs/tiny-llm-zh/lib/python310.zip
/home/xx/micromamba/envs/tiny-llm-zh/lib/python3.10
/home/xx/micromamba/envs/tiny-llm-zh/lib/python3.10/lib-dynload
/home/xx/micromamba/envs/tiny-llm-zh/lib/python3.10/site-packages

可以看到 python test0/test.py 将文件所在文件夹加入了 sys.path,而 python -m test0.test 将 cwd(当前工作路径)加入了 sys.path