包的概念:
包就是一个包含__init__.py文件的文件夹,是一种特殊的模块(是用来被导入使用的)
为什么使用包:
假如你是一个模块的设计者,随着功能的不断完善,一个.py文件内可能存在N多个函数,这时就会出现不易于管理的问题,那么你想到的是根据功能的划分,将相同功能的代码放在一个.py文件,不同的分放到不同的.py文件中,但是对于使用者来说这种简简单单的划分,更改了使用者的调用方式(原来所有功能可能都在一个m1.py中,使用者仅仅需要导入m1这个模块即可),但是现在使用者要明确哪个功能在哪个模块内,还有调用相应的模块,所以出现了包(用来组织文件的管理)
包的使用:
如果说包是一种文件夹,也是一种模块,而对于首次导入模块发生的三件事:
1:创建模块的名称空间 2:执行模块内的代码,将执行过程中产生的名字存放在模块的名称空间中 3:在执行文件中拿到一个变量名,该变量名指向模块的名称空间(以import导入为例)
那么,包这个特殊的模块导入时发生了什么?(文件夹是不能放代码的...如何产生名字?)
python在我们创建包时,自动在包内为我们添加了一个名叫__init__.py的文件,so,导入包时发生的事情:
1:以包下的__init__.py文件为基准,产生一个名称空间 2:执行包下的__init__.py文件,将执行过程中产生的名字存放在产生的名称空间中 3: 在执行文件中(不是__init__.py)拿到一个名字,该名字指向__init__.py的名称空间
所以,要明确的是,导入包实际上是在导入包下的__init__.py文件
然后,假如你的包是这样的
那么使用者如何在run这个执行文件中调用m1呢?(在run文件添加sys.path?不可能!使用包还要让人添加环境变量?)
可以这样,通过__int__导入,内容为:
#m1.py内容: def f1(): print('m1.f1') #m2.py内容: def f2(): print('m2.f2') #__init__.py的内容: from p.m1 import f1 from p.m2 import f2 #注意绝对导入时必须是从p这个包开始导入,因为参照的是执行文件的sys.path,直接导入m1会报错 #run.py的内容: import p p.f1() p.f2() #运行结果:---> m1.f1 m2.f2
感觉似乎万事大吉了,以后直接在__init__内添加导入语句即可,but,包下载下来,假如使用者放在了不和执行文件同一个文件夹下内(一个C盘,一个D盘),就像这样:
解决方法当然是得使用者自己在这些文件中添加环境变量啦:
import sys sys.path.append(r'D:\Program Files\python全栈\学习日记\模块篇\dir1') import p p.f1() p.f2()
绝对导入与相对导入
当我们在扩展包的过程中可能要更改包名(这样绝对导入的方法就得更改包下的内容了)
那么我们可以使用相对导入解决问题
内容:
#p的__init__.py from .m3 import f3 from .p1.m1 import f1 from .p1.m2 import f2 #m1.py from ..m3 import f3 def f1(): print('m1.f1') f3() #m2.py def f2(): print('m2.f2') #m3.py def f3(): print('m3.f3')
通过点表示当前文件(非执行文件)的文件夹,点点表示上一级文件夹来实现更好的扩展性,无论顶层包名更改为什么,都不会影响包内部
总结:
1.导包实际上是在导包下面的__init__.py文件
2.如果使用绝对导入,绝对导入的起始位置应该是以包的顶层目录为起始点
3.包内部模块的导入通常应该使用相对导入,用.代表当前所在的文件(而非执行文件)
强调:相对导入只能在包内部的模块之间
4.但凡导入语句中带点的,点的左边必须是一个包,这是导入包特有的
5.是from ...import....,import后必须是一个明确的名字,没有任何前缀
此外:在python2中包下面必须有__init__.py文件,pyhton3没有不会报错