最近写代码的时候又遇到了Import出错的问题,以前遇到这类问题总是赶紧Google一下,尽快找到解决方案,最近又遇到了这个问题,小小的研究了一下,借这个机会稍微总结一下import的用法。
python import原理
要想用根本上解决这个问题,就必须了解import的工作原理。稍微Google了一些,在执行import时分如下几步进行:
- 创建一个新的、空的 module 对象(它可能包含多个 module);
- 将该 module 对象 插入 sys.modules 中;
- 装载 module 的代码(如果需要,需先编译);
- 执行新的 module 中对应的代码。
import会出错问题就在于第三步。首先会找到module所在的位置,其搜索顺序是:
sys.path -> PATHONPATH -> python默认路径。我们最主要使用的就是sys.path了。sys.path是一个list,是import的搜索路径, python程序运行时会自动将该入口程序的所在目录添加到sys.path的首位;然后就是python的第三方库的安装路径。如图所示:
因为这个机制,就产生了很多的问题。当程序比较大的时候,因此import只关心入口程序的path以及第三方库,所以在使用一些自定义的package时就会出现无法找到的错误。
import的几种不同的使用情况
同级目录 不在包内
项目结构如图所示:
这里file0.py和file01.py是在项目的根目录,也就是最简单的情况
代码分别如下:
# file0.py
a = 1
# file01.py
from file0 import a
print(a)
代码非常简单,两个文件在同级根目录下,运行成功。
同级目录下的包
结构如图:
代码如下:
# file0.py
from test1 import file1
file1.pr(1, 2)
# test1/file1.py
def pr(x, y):
print(x+y)
这种情况是比较常用的情况,file0.py作为入口函数,调用其子目录的包里的module。代码运行成功
隔壁目录下的包文件
结构如图:
代码如下:
# test1/file1.py
def pr(x, y):
print(x+y)
# test2/file2.py
from test1 import file1
file1.pr(1, 1)
运行代码发生错误:
分析一下原因,由于import是根据sys.path来搜索的,而sys.path添加的又是入口程序的目录,在当前目录下当然找不到同级文件夹了。在入口代码加上输出sys.path的代码,结果如下:
我们看到第一个路径就是当前入口程序的所在目录,与之前分析符合。
如何解决这个问题?既然知道了import的工作原理,最直接的想法就是把test1的路径加到sys.path里,但是不太好实现;退一步,我们只要把test2和test1的父目录加到sys.path里就可以了。修改代码:
# test1/file1.py
def pr(x, y):
print(x+y)
# test2/file2.py
import sys
sys.path.append('..')
from test1 import file1
file1.pr(1, 1)
代码运行成功。
复杂情况
文件结构不变:
代码如下:
# test2/file2.py
import sys
sys.path.append('..')
from test1 import file1
file1.pr(1, 1)
# test1/file1.py
import file11
def pr(x, y):
print(file11.add(x, y))
# test1/file11.py
def add(x, y):
return x+y
运行代码,出现错误:
在这里上一个方法就行不通了,分析一下:
入口文件还是file2.py,因此sys.path里应该有test2文件夹,我们又手动加入了’..’,因此还包括了test2的父文件夹。因此,我们from test1 import file1
成功了;接下来,在file1里,我们想要import同级module file11.py 。我们直接使用import file11
,看起来是没有问题的,但是程序也就是在这里报了错。分析一下,因为我们是直接使用的import file11
操作,也就是直接在sys.path中搜索file11,当然是搜索不到的。想要使用到file11,sys.path必须要包含test1路径。
找到了问题的所在,我们只要对症下药即可。我们再把test1的路径加入sys.path不就可以了。
代码如下:
# test1/file1.py
import sys, os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
print(BASE_DIR)
sys.path.append(BASE_DIR)
import file11
def pr(x, y):
print(file11.add(x, y))
总结一下,就是在这个包的入口文件处,将这个包的目录加入到sys.path中即可。这里添加的是绝对路径,因此在其他位置调用该包也可以正常工作。
小结
基本上常见的情况就是这些了,其实对于这些问题,有一个更加简便的方法:使用pycharm pycharm在使用的时候,会自动把项目的根目录加到sys.path里,上述问题自动解决🤪(pycharm真相)所以说写项目的时候还是用pycharm吧!
2022.11.9更新
这个问题真是太常见了,总是遇到,干脆置顶吧