在同一级目录下新建 p1.py 和 run.py,添加代码python
# p1.py 模块的设计者
def f1(): print("from f1") def f2(): print("from f2") def f3(): print("from f3") # run.py 模块的使用者
import p1 p1.f1() p1.f2() p1.f3()
假设后期须要添加许多功能,可能添加的功能与已有的功能之间还有关联,这对于模块的设计者是很不方便的,因而设计者建立多个文件,把相关的功能放入同一个文件,这里我添加m1.py,m2.py,m3.py,将f1,f2,f3分别放入相关的功能文件app
# m1.py
def f1(): print("from f1") # m2.py
def f2(): print("from f2") # m3.py
def f3(): print("from f3")
但这样对于使用者来讲又不方便了,原先的使用方式当然已经改变,因而新建一个p1的文件夹,将m1.py,m2.py,m3.py放入p1,这样对于使用者看起来仍是只有一个模块,使用的时候仍是p1.f1(),p1.f2(),p1.f3()等ide
之前导入的p1是一个模块,如今导入的 p1 是一个文件夹(包),因而如今的问题是将这个包可以像以前的模块同样可以被导入和使用spa
1、什么是包设计
包就是一个包含有 __init__.py 文件的文件夹,本质就是一种模块,即包是用包导入使用的,包内部包含的文件也都是用来被导入使用3d
2、为什么要用包code
包是文件夹,那文件夹就是用来组织文件的blog
3、包的使用内存
首次导入包,发生三件事:博客
一、以包下的 __init__.py 文件为基准来产生一个名称空间
二、执行包下的 __init__.py 文件的代码,将执行过程当中产生的名字都放入名称空间中
三、在当前执行文件中拿到一个名字,该名字就是指向__init__.py 名称空间的
注意:
一、在python3中,即便包下没有 __init__.py 文件,import 包仍然不会报错,而在python2中,包下必定要有该文件,不然import 包报错
二、建立包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块
新建包 p1 和 run.py,在包下新建 m1.py,在 m1.py 中添加代码
# m1.py
def f1(): print('m1.f1')
如今我想在 run.py 中经过 p1.f1() 调用到 m1 中 f1 的功能,但此时是调用不到的,由于经过 p1 调用 f1 就是经过 __init__.py 调用 f1,__init__.py 中是没有 f1 的,f1 是在 m1 中,因此要在 __init__.py 中导入 m1,首先不能直接 import m1,由于还须要拿到 m1 下的 f1 功能,因而可能有人会想,那我直接 from m1 import f1 就好了,但这样也是不行的,由于内存中没有 m1,内置模块也没有 m1,因而在 sys.path 中查找,sys.path 是以执行文件是 run.py 为准,run.py 的 sys.path 的第一个值是上一级目录,这个目录中并无 m1 模块(m1在p1中),因此直接 from m1 import f1 报错
又有人会想,那我把 m1 模块所在的文件夹(p1)添加到 sys.path 不就好了,因而我在 run.py(sys.path是以执行文件是run.py为准)中添加环境变量
import sys sys.path.append(r'E:\Python\p1') import p1 p1.f1()
这样作确实可以实如今 run.py 中经过 p1.f1() 调用到 m1 中 f1 的功能,可是这对于使用者来讲是很不方便的,每次都要添加内部功能所在文件夹的环境变量,因此这样作也是不合适的。
能够在 __init__.py 中以 p1.m1 的方式导入 f1,这样在 run.py中就能够直接导入 p1 而后 p1.f1() 执行
# __init__.py
from p1.m1 import f1 # run.py
import p1 p1.f1()
假设 run.py 和 p1 不在同一级目录下,如今我新建一个 dir1,在 dir1 下新建一个 dir2,将 p1 放入 dir2,那这时在执行 run.py 时,就须要在 run.py 中添加环境变量了,有人可能有疑问,上面不是说添加环境变量不方便使用者吗,注意,我在这里添加的环境变量不是 p1 内部功能所在文件夹的环境变量,而是找到 p1 所在文件夹的环境变量,这就至关于用户下载这个程序本身选择的保存位置,在这个位置下能够找到 p1。因此我在这里只需将 dir2 的位置添加到环境变量便可
# run.py
import sys sys.path.append(r'E:\Python\dir1\dir2') import p1 p1.f1()
上面的导入也称之为绝对导入,每次都是参考执行文件的 sys.path 开始去导入
软件每次更新都有不一样的版本,设计者也须要将包更名,例如 p1_v1,p1_v2 等,若是使用的是绝对导入,那包内其它的导入都须要改变一次,因此包内的模块不该该使用绝对导入,应该使用相对导入
因此,在本片博客开始留下一个问题,将包像模块同样可以被导入和使用,因而在 p1 下新建一个 __init__.py 文件,使用相对导入
# __init__.py
from .m1 import f1 from .m2 import f2 from .m3 import f3
而后直接在 run.py 中导入 p1 即可以模块同样被使用
# run.py 模块的使用者
import p1 p1.f1() p1.f2() p1.f3()
如今我在同一级目录新建一个包 p1 和 run.py,在 p1 下新建包 p2,m1.py 和 m2.py,在包 p2 下新建 m3.py
# m1.py
def f1(): print('m1.f1') # m2.py
def f2(): print('m2.f2') # m3.py
def f3(): print('m3.f3')
如今仍是想实现与博客开始相同的功能,经过 p1.f1(),p2.f2(),p3.f3() 访问功能,作法也与上面的相同,在 p1 的 __init__.py文件中导入相关模块便可
# p1的__init__.py
from .m1 import f1 from .m2 import f2 from .p2.m3 import f3
如今我想在 m3.py 的 f3 中访问到 f1 和 f2,因而在 m3 中须要导入 m1 和 m2
# m3.py
from ..m1 import f1 from ..m2 import f2 def f3(): print('m3.f3') f1() f2()
这时候即使 p1 更名 p1_v1,在 run.py 中导入调用 f3,包内的导入也无需更名
# run.py
import p1_v1 p1_v1.f3()
总结:
1. 不管是 import 形式仍是 from...import 形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提升警觉:这是关于包才有的导入语法,点的左边都必须是一个包
二、包的本质就是一个包含 __init__.py 文件的目录,导入包就是在导包下的 __init__.py 文件
三、若是使用绝对导入,绝对导入的起始位置都是以包的顶级目录为起始点。可是包内部模块的导入一般应该使用相对导入,用一个点表明当前所在的文件(而非执行文件),两个点表明上一级,须要强调的是,相对导入只能在包内部的模块之间互相导入使用,使用多个点往上查找时不能超出顶级包