python 子包调用 跨目录调用
1. python3中的模块和包
简单来讲,模块是一个包含Python定义和语句的文件,包是一种通过用“带点号的模块名”来构造 Python 模块命名空间的方法。
详细可以参考廖雪峰的:python教程-模块
本文以以下的项目结构,对python3的子包之间的调用进行探索:
使用的python版本:3.7.0
平台:win10
2. python的模块搜索路径
当一个名为 spam 的模块被导入的时候,解释器首先寻找具有该名称的内置模块。如果没有找到,然后解释器从 sys.path 变量给出的目录列表里寻找名为 spam.py 的文件。sys.path 初始有这些目录地址:
- 包含输入脚本的目录(或者未指定文件时的当前目录);
- PYTHONPATH (一个包含目录名称的列表,它和shell变量 PATH 有一样的语法);
- 取决于安装的默认设置;
3. 调用子包与调用兄弟包
假如在main.py中调用pack.mod1.py中的函数func1,而func1又调用pack2.mod2.py中的func2函数,各个文件中的代码如下:
main.py:
import sys print("file:{},sys.path:{}".format(__file__, sys.path)) from pack1 import mod1 if __name__ == "__main__": mod1.func1()
pack1/mod1.py:
from pack2 import mod2 def func1(): print("this is pack1.mod1") mod2.func2()
pack2/mod2.py:
def func2(): print("this is pack2.mod2")
进入test_mod目录,执行命令:
python main.py
输出:
file:main.py,sys.path:['D:\\1-Work\\python_src\\test_mod',
'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\xxx\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages'] this is pack1.mod1 this is pack2.mod2
可以看出sys.path中的第一个元素,就是main.py所在的目录,因为包pack1和pack2都在这个目录下,所以它们能够互相发现。
假如这个时候,你想单独调试一下pack1.mod1.func1函数,将mod1.py修改如下:
import sys print("file:{},sys.path:{}".format(__file__, sys.path))
from pack2 import mod2 def func1(): print("this is pack1.mod1") mod2.func2() if __name__ == "__main__": func1()
进入pack1目录运行:
python mod1.py
输出如下:
file:mod1.py,sys.path:['D:\\1-Work\\python_src\\test_mod\\pack1',
'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\xxx\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages'] Traceback (most recent call last): File "mod1.py", line 3, in <module> from pack2 import mod2 ModuleNotFoundError: No module named 'pack2'
发现sys.path的第一个路径已经是test_mod/pack1了,在这个目录下面解释器无法找到pack2这个包。
如果需要发现这个包,按照python搜索路径的规则,需要将test_mod这个目录加入到sys.path中,一般来说可以:
- 通过环境变量PYTHONPATH进行设置;
- 也可以将路径写入.pth文件,Python在遍历已知的库文件目录过程中,如果见到一个.pth 文件,就会将文件中所记录的路径加入到 sys.path 中;
- 最简单的是使用sys.path.append函数将路径加入。
使用最后一种办法,将pack1.mod1.py修改为:
import sys sys.path.append("D:\\1-Work\\python_src\\test_mod") print("file:{},sys.path:{}".format(__file__, sys.path)) from pack2 import mod2 def func1(): print("this is pack1.mod1") mod2.func2() if __name__ == "__main__": func1()
结果输出:
file:mod1.py,sys.path:['D:\\1-Work\\python_src\\test_mod\\pack1', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\xxx\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages', 'D:\\1-Work\\python_src\\test_mod']
为什么使用绝对路径呢,因为如果使用相对路径的话,当前工作路径一变就不行了,不信在test_mod目录执行
python pack1/mod1.py
或者用vscode等文本编辑器打开test_mod目录,然后直接F5运行,工作目录就是test_mod,而不是pack1,这个时候添加的”../”路径就会变成目录python_src。
这也是为什么很多文章使用sys.path.append(“../”),然后很多人在留言区说在他们本地运行的时候根本不行。
所以一切的关键还是模块搜索路径。
回到解决方式上面,或者将项目的路径加到PYTHONPATH环境变量,或者写入.pth并放在搜索路径中才是一劳永逸的方式。
4. 参考
(1) python3文档-模块
(2) 廖雪峰:python教程-模块
(完)