我们在开发项目时,在使用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
中了