一、背景
1 问题描述
在实战测试平台开发时,服务文件启动报错。
我的项目中,utils包下创建了一个service.py文件,用来启动服务。
service.py文件中的方法引用了其他模块,而有一个包名也叫“service”,导致这部分的引用报错,提示“ModuleNotFoundError”。
报错如下图:
2 包、模块与文件
这个问题中,一会儿包名(package),一会儿模块(module),一会儿文件(.py后缀的等),概念十分混淆。
2.1 Package包
在我上述截图中,controller、dao等都属于package包。
特点:
- 包下面一定有一个__init__.py文件,用于程序初始化(可以写入内容,也可以不写);
- 导包引入模块时,写法为:from package名.文件名 import 具体函数or类;
从Pycharm创建入口看:
它创建的就是一个“Python Package”。
2.2 Module模块
官方定义:一个.py文件就可以被看做是一个module模块。
两种引用方式:
- import 模块名;
- from 模块名 import 函数名/类名;
比如,有一个mymodule.py文件:
# mymodule.py
def greet(name):
print(f"Hello, {name}!")
然后,在另一个文件中引入 mymodule 模块,并调用 greet 函数:
# main.py
import mymodule
mymodule.greet("Python")
也可以使用 from mymodule import greet 来引入 greet 函数:
# main.py
from mymodule import greet
greet("Python")
二、排查总结
1 初步猜测原因
我执行的是service.py文件,根据报错提示信息,引入模块导包的语句错误,service是个文件而不是个包(package)。
在我整个项目结构中,service是个文件的仅有截图那个位置,于是大胆猜测:
引入模块的语句“service.user_service”没有被识别,即service没有被认为是个包(package)。
即便我的引入写法并没有错误。
2 寻找原因佐证
于是,我开始搜索,来找到可以佐证这种冲突的论据,最好是官方文档说明了解析优先级啥的。
最终,在Python官方文档的“Module”章节,找到了“The Module Search Path”。
官方文档地址:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
模块的搜索路径逻辑:
- 程序初始化后,引用的模块会首先在“内置模块名”当中查找;
- 如果查找不到,则在sys.path系统路径中查找,其包含了当前工作目录、Python安装目录、PythonPath配置路径等;
以上搜索路径的逻辑,只能说明在查找模块路径时的优先级,并不能说明文件名和包名重名的情况下,为什么会出现冲突,于是继续查找论据。
在该段落末尾找到了说明:
- 在初始化完成后,程序可以修改sys.path,意思就是说,sys.path的路径不是一成不变的;
- 在运行的脚本所在路径,会在查找路径中优先级处于最高,意思就是说,当前执行的service.py所在路径查找优先级最高,高于其他同名的包、模块;
官方文档原文:
结合以上2点,总结这次报错的原因为:
- 执行service.py时,因其查找优先级最高,已经将service定义为了“文件”;
- 因此在导包写法“service.user_service”中,service应该是package包名,但无法被识别为包,所以出现截图中的报错。