python包(模块)
什么是包
包是模块的集合。如果把模块比成一个个的python文件,那包就是一个文件夹,里面存放一堆的python文件。
学习包要从两个角度来看,包的设计者和包的使用者。因为包是模块,所以包的使用和普通模块的使用方式是一样的。
还需要注意,包的目的不是为了运行,而是为了被导入使用。包只是模块的一种形式,本质上包也是模块。
# 包是一个包含__init__.py文件的文件夹。
# 对于普通模块(一个py文件),会发生三件事,其中一件事就是执行模块文件的代码。
# 包是一个文件夹,不能是普通模块那样被执行代码,所以给包提供了一个__init__.py文件,导入包就会执行__init__.py文件,这也是__init__.py文件村的意义。
# python3中,文件夹没有__init__.py也可以,但是在python2中包必须要有该文件。
使用包
使用包是通过导入的方式使用,使用包和使用普通模块的方式一样,首次导入包也会发生三件事
#1 执行包的__init__.py文件;
#2 产生一个名称空间,即__init__.py的名称空间,用于存放__init__.py文件执行过程中产生的名字;
#3 在当前执行文件所在的名称空间中放一个包的名字,该名字指向__init__.py的名称空间。
# import 包:会将包的__init__.py文件执行一遍,得到的名字都是该__init__.py文件中存在的名字。
# 也就是说包里面的子包或子模块如果不在__init__.py中,是不会被执行的,就不会被导入。
导入包就是导入包内的__init__.py文件
强调三件事:
#1 包的导入方式和模块的一样:import和from...import..两种
- 无论何种方式,无论任何位置,导入时带点的,点的左边都必须是一个包。使用时不限制。
#2 包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
#3 import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
设计包
作为包的设计者,需要关注两件事:方便自己组织管理程序;给包的使用者提供便利。
首先,方便自己组织管理程序。可以将不同功能的代码放在放在不同的模块(py文件)或者不同的子包(子文件夹)中,这样可以结构清晰的管理组织自己的模块功能。
其次,如何给使用者提供使用包的便利。使用者一般都最求简单的使用方式。所以包的设计者最好将使用方式尽可能设计的简单清晰。因为使用者使用包的方式和使用模块的方式一模一样的,import ...
,或 from....import....
的方式。不论那种方式,都会执行模块的程序代码。所以如果是包的化,就需要在__init__.py
中隐藏好导入的关系,方便使用者使用。
# 包内有很多子包,子包内有一些模块文件, 使用者想通过:包.名字 的方式使用包内所有的名字。
# 此时可以通过:包内模块间的相互导入,可以隐藏包内个模块之间的关系,给使用者提供便利。(这样真的便利吗?)
包内模块的绝对导入
绝对导入:以顶级包作为起始位置。
# pool包的__init__.py文件
from pool.subpool import f1 # 必须从点击包开始
f1() # 使用
补充:import也能使用绝对导入,导入过程中同样会依次执行包下的__init__.py,只是基于import导入的结果,使用时必须加上该一堆前缀。
import pool.subpool.f1
pool.subpool.f1() # 使用
包内模块的相对导入
相对导入:.
代表当前文件所在目录,..
代表当前目录的上一级目录,以此类推。
# pool下的__init__.py
from .subpool import f1
补充:包内模块相互导入只能使用from ... import ... ,import ... 不能使用,语法错误
# 强调:包内模块的相互导入推荐使用相互导入
- 相对导入只能包内使用,跨出包不能使用时非法的
- 无论是import.. 还是from .. import .. ,只要导入时带点的,点的左边必须是包
补充
作为包的使用者,导入包的方式有:from 包 import *
,此时 * 表示的是将包下的__init__.py中的所有名字。包的设计这可以通过列表__all__
来控制*代表的意思
# 包的__init__.py文件
__all__ = ['f1', 'f2']
# 此时通过from 包 import *,导入的只有f1和f2