目录
1. 项目入口文件
2. 绝对导入
Python本地模块的引入是比较简单的,包括绝对引用和相对引用(相对引用的博客参考下一篇Python基础 - 本地模块的相对导入/引用),个人比较推荐使用绝对引用,会避免掉很多错误,本篇文章主要介绍绝对导入。
项目入口文件一般要放在项目根目录下,如下图1红框main.py所示,即为项目入口文件。如果运行main.py,系统会自动把入口文件所在的根目录,加入到sys.path里。这样在以引用方式导入包时,会逐一查找sys.path中的路径,第一个即为项目根目录,如下图2所示,其中sys.path的路径包括:[项目根目录。python的压缩包路径。python自带的一些库如os, json等。so库文件。使用pip/conda安装时的第三方库]
绝对导入是比较简单的,也比较推荐大家采用这种方式。首先根据上图画出当前的文件树,如下:
Import_Test
|--dir1
|--d11.py
|--d12.py
|--dir2
|--d21.py
|--d22.py
|--test.py
首先我们明确一点的是,如果运行哪个文件,该文件所在的父目录就会被加载到sys.path中,在绝对导入模块时就以这个父目录为依据来查找文件夹/文件
示例1:引入入口文件的同级目录文件
# test.py ----------------------------
from dir1.d11 import d11_f
def test_f():
print("test")
if __name__ == '__main__':
test_f()
d11_f()
# dir1/d11.py ------------------------
def d11_f():
print("d11_f")
# 运行结果如下:
(base) @118:~/Wmq/Import_Test$ python test.py
test
d11_f
从层级上看,test.py在根目录下,所以在运行时,test.py的父目录/Wmq/Import_Test被加载到sys.path中,所以引用dir1下的d11模块时,直接引用dir1,因为该目录在/Wmq/Import_Test下。
示例2:test.py没变,引入的子模块dir1.d11同时引入同级模块dir1.d12
# test.py ----------------------------
from dir1.d11 import d11_f
def test_f():
print("test")
if __name__ == '__main__':
test_f()
d11_f()
# dir1/d11.py ------------------------
from dir1.d12 import d12_f
def d11_f():
print("d11_f")
d12_f()
# dir1/d12.py ------------------------
def d12_f():
print("d12_f")
# 运行结果如下:
(base)@118:~/Wmq/Import_Test$ python test.py
test
d11_f
d12_f
同级模块的引入是比较好理解的,根据示例1,test.py在运行时,其父目录/Wmq/Import_Test被加载到sys.path中,所以无论怎么调用d12_f,都要在/Wmq/Import_Test的基础上引入,而dir1就在盖目录下,所以就是dir1.d12。
示例3:test.py没变,引入的子模块dir1.d11同时引入跨文件夹模块dir2.d21
# dir1/d11.py ------------------------
from dir2.d21 import d21_f
def d11_f():
print("d11_f")
d21_f()
# dir2/d21.py ------------------------
def d21_f():
print("d21_f")
# 运行结果如下:
(base)@118:~/Wmq/Import_Test$ python test.py
test
d11_f
d21_f
从示例不难发现,在dir1文件下的d11.py中,是如何跳跃文件夹引入dir2文件下的d21.py呢?是直接导入dir2文件夹。为什么这样不报错,原因也在示例1/2中有解释,test的运行加载了父目录/Import_Test,而dir2就在父目录下,所以直接引入dir2即可。
示例4:如果不运行test.py了,只运行dir1/d11.py, 其他任何引用不变,结果如下:
# dir1/d11.py -------------------
from dir2.d21 import d21_f
def d11_f():
print("d11_f")
d21_f()
if __name__ == '__main__':
d11_f()
# 运行结果报错,如下:
(base) zhangyujun@118:~/Wmq/Import_Test/dir1$ python d11.py
Traceback (most recent call last):
File "d11.py", line 1, in
from dir2.d21 import d21_f
ModuleNotFoundError: No module named 'dir2'
运行报错了,这其实在情理之中。因为我们前面说过,如果运行哪个文件,该文件所在的父目录就会被加载到sys.path中,在绝对导入模块时就以这个父目录为依据来查找文件夹/文件。那当我们运行d11.py时,d11.py的父目录也就是dir1被加载到sys.path中,然后顺着dir1往下找,肯定找不到dir2的,所以报错。我们验证下sys.path看一下是不是dir1被加载进去了,如下:
# dir1/d11.py -------------------
import sys
print(sys.path)
from dir2.d21 import d21_f
def d11_f():
print("d11_f")
d21_f()
if __name__ == '__main__':
d11_f()
# 运行结果:
(base) zhangyujun@118:~/Wmq/Import_Test/dir1$ python d11.py
['/home/zhangyujun/Wmq/Import_Test/dir1',
'/home/zhangyujun/anaconda3/lib/python38.zip',
'/home/zhangyujun/anaconda3/lib/python3.8',
'/home/zhangyujun/anaconda3/lib/python3.8/lib-dynload',
'/home/zhangyujun/anaconda3/lib/python3.8/site-packages']
Traceback (most recent call last):
File "d11.py", line 3, in
from dir2.d21 import d21_f
ModuleNotFoundError: No module named 'dir2'
确实,sys.path中的第一个是dir1目录,自然找不到dir2目录,那怎么解决这种问题呢?我们可以在sys.path里添加上根目录,运行结果即可正常,如下:
# dir1/d11.py -------------------
import sys
sys.path.append("/home/zhangyujun/Wmq/Import_Test")
from dir2.d21 import d21_f
def d11_f():
print("d11_f")
d21_f()
if __name__ == '__main__':
d11_f()
# 运行结果:
(base)@118:~/Wmq/Import_Test/dir1$ python d11.py
d11_f
d21_f