包的导入
- 包的概念
- 包的使用问题
- 包结构与__init__.py文件
- from XXX import *
- 绝对导入与相对导入
- 包内部的相互导入/python解释器眼中的顶级包
—:包的概念
可以简单的理解为包就是一个包含着__init__.py文件的文件夹,里面包含着多个文件,这些文件中又包含着多个模块,也就是我们的py文件。
随着我们代码量以及功能逐渐的多元化与完善,我们再将全部的模块同一放到一个py文件下显然已经不再合适。因此,我们需要根据模块的功能和属性将其分类,分别存放于不同的文件之中,而我们的包就是将这些文件给包裹起来,对外形成一个统一体。
在python的机制中,为我们提供了一个整合模块与子包(子文件夹)的功能:包
foo就是一个包,里面包含着子包bbb与__init__.py文件,m1,m2,m3就相当于其中的模块。
包的使用
1.包的结构:总的来说包必须包含__init__.py文件,但在python解释器中带有__init__.py文件的文件夹却不一定被视为包。
2.__init__.py文件的作用:导入包的操作其实就是执行包内的__init__.py文件。
总体上每次导入包相当于执行了三件事情
1:执行包内的__init__.py文件
2:生成一个名称空间用来存放加载__init__.py时产生的名字(变量名,函数名...)
3:在导入包的名称空间内产生一个名字(一般为包名),该名字指向第二步产生的名称空间,也就是说导入包只是将__init__.py文件下的名字导入到内存中,并不是将包内所有的模块与子包导入其中。
from XXX import *
相当于将__init__.py文件下的导入所有名字。日常使用中不建议这样使用。一般模块的提供者会用__all__来控制外界用户所能使用的功能。
比如:__all__['m1','m2','m3']
绝对导入与相对导入
1.绝对导入
从顶级包开始:from foo.m1 import f1
2.相对导入
**.** 代表当前文件,**.** **.**代表上一级文件,依次类推
如:from . m1 import f1
绝对导入例子:
此时foo作为顶级包,内含有m1~m3模块和子包bbb,
若我们在foo同级目录下的一个文件中调用foo里面的功能,则为:from foo import m1
即从顶级目录下走,一步步一层层的找到我们所需要的的功能函数
相对导入例子
相对导入的优势:
从绝对导入的概念中可以感受到包还是模块的导入方式都可以采用绝对导入来实现的,
那么为何python给我们提供了另一种包的导入方式呢?假设我们开发的包功能越来越多,越来越完善
我们现今的foo包成为了一个子包,那么我们在包内采用的绝对调用方式将会发生整体性的变化。
我们在包内部采用绝对导入的方式由于顶级包发生变化将全部发生改变,而相对导入严格依照**.**,无需顾忌顶级包的变化,仅需在最外层调用进行修改即可
包内部的相互导入
如果我们在foo包内的m2文件中需要用到bbb子包中m4文件中功能时又该怎么办?
法一 : m2文件中绝对导入 from foo.bbb import m4
法二:m2文件中相对导入 from .bbb import m4
但这样不难发现我们无法直接运行m2文件,绝对导入中m2与bbb同一级别,其索引目录里面找不到包foo。存在相对导入的文件无法执行运行。
那么我们如何可以直接运行m2文件又可以在外部调用包的情况下不产生错误呢?
sys内置模块,sys.path里面可以显示我们当前文件执行时的解释器索引目录,绝对导入时因找不到bbb包(执行哪一级下文件只能找到同级目录的文件信息)而产生错误,我们只需要将bbb包的父目录路径添加进索引目录即可
sys.path.append(包foo的绝对路径)
python解释器眼中的包
一般来说,带有__init__.py文件的文件夹都可以视为一个包,但是在python解释器中对顶级包的定义有一些不同。
当我们将main.py作为主函数窗口执行时,此时其父级目录a(尽管含有__init__.py文件)此时将不被视为顶级包
此时在使用相对导入就容易报出超出顶级包这一错误。这也是为什么模块都以一个整体包呈现出来的部分原因,
我们在模块包外部进行导入从而能有效避免包内部相对导入而可能产生的相关错误
总之就是,一般最外层包内的子文件作为调用窗口,则最外层包则被解释器视为寻常文件夹,不能作为整个模块的顶级包使用。