我们在开发项目时,在使用pycharm继承好的环境下,直接点击按钮运行代码,一般不会遇到自己的包找不到的错误。但是当我们在使用命令行运行python代码,在项目的根路径下运行python xxx可能会报错找不到某个模块。ModuleNotFoundError: No module named 'xxxx'
前言
在介绍这类问题前,不知道你们有没有好奇过。我们平时在python中使用下面的代码调用包时,python是如何找到这些包的? 把这个问题弄清楚,这类问题解决起来就很明白了。
1 | import os,sys |
python是通过模块搜索找到这些包的
python模块搜索路径
当一个名为xxx的模块被导入的时候,解释器会在下面的路径里搜索:
- 内置模块
- 包含输入脚本的目录(或者未指定文件时的当前目录)
- PYTHONPATH(一个包含目录列表,它和shell变量有一样的语法)
- pip安装的第三方库
- ……
上面所有的搜索路径会初始化在sys.path中。
与自定义包找不到的关系
了解了python模块搜索的过程,我们再解决自己的包找不到的问题,思路就很清楚了。只要将自己写的包添加在sys.path中就行了。
模块和包的定义
- 模块:python中每个
.py文件称为一个模块 - 包:每个具有
__init__.py文件的目录被称为包
只要自定义的模块或者包所在的目录在sys.path中,就可以使用import导入。(原因在上面已经解释)
几种常见的包导入情况
同级模块的导入
main.py和test.py都在blog_test目录下。在main.py中直接引用test.py后得到的输出如下:
同级目录下模块的导入
导入单个模块
main.py和pakage1同级,pakage_a.py是pakage1下面的一个模块
所有模块都导入
main.py想使用from pakage1 import *将pakage1中所有模块导入。需要在pakage1下面创建__init__.py文件,并在里面将pakage1中所有包导入。
跨级目录的导入
问题描述
跨级目录的导入是最麻烦的
我们在这里想在pakage2/pakage_b.py中导入pakage1/pakage_a.py,但是可以看出得到报错,找不到pakage1这个包。
在这里将sys.path打印出来,发现第一个路径是pakage2这个包的绝对路径。python在路径搜索的时候就会在pakage2下面搜索。自然是找不到pakage1这个包的。
同样的,这时如果我们在pakage_b.py中导入main.py也是报错
注意: 这个问题如果是使用pycharm中的run直接运行的,则不会出现bug,因为pychram添加了在sys.path中添加了一个路径:D:\JetBrains\PyCharm 2020.1.1\plugins\python\helpers\pycharm_matplotlib_backend。但是实际部署项目我们是通过命令行/终端,这个问题就会出现。
解决方法
我们只需要将pakage1这个包的路径添加到sys.path中就行了,其实我们也可以把项目blog_test的路径添加到sys.path中来解决这个问题,因为会递归查找包。
可见添加的一行代码是,网上一般用绝对路径,这里写成相对路径,更好些。
1 | sys.path.append(os.path.split(sys.path[0])[0]) |
深入一点
对于上述报错的代码,我们修改一点,在main.py中导入pakage2/pakage_b.py这个模块,然后使用python main.py运行该模块,会发现不报错。
解释:python是将运行的这个.py文件父文件夹的路径添加到sys.path中
结论
导入自己的包出错的问题,一直困扰了我很长时间,现在终于弄明白了。以后再写python项目的时。如果出现包导入错误,可依次进行如下排查:
- 文件夹下面有没有
__init__.py文件来声明该文件夹是个包。 - 是否是跨目录导入,如果是,是否把路径添加到
sys.path中了



