微信公众号搜索【程序媛小庄】,关注半路出家的程序媛如何靠python开发养家糊口~
前言
随着模块数目的增多,把所有模块不加区分地放到一起就显得非常不合理了,于是Python为我们提供了一种把模块组织到一起的方法,即创建一个包。
学习包要从两个角度来看,包的设计者和包的使用者。因为包是模块,所以包的使用和普通模块的使用方式是一样的。
什么是包
包就是一个包含__init__.py
文件的文件夹,文件夹内可以包含子包或者模块,比如创建如下结构的包结构:
package # 包文件夹
|—— __init__.py
|—— small_package # 子包文件夹
| |—— __init__.py
| |—— module1.py # 模块文件
| |—— module2.py # 模块文件
|——module3.py # 模块文件
创建包的目的不是为了运行,而是为了被导入使用,包的本质就是模块,因此可以将包当做模块来导入。对于普通模块(一个py文件),会发生三件事,其中一件事就是执行模块文件的代码。包是不能像普通模块那样被执行代码,所以包提供了一个__init__.py
文件,导入包就会执行__init__.py
文件,这也是__init__.py
文件存在的意义。python3中,文件夹没有__init__.py
也可以,但是在python2中包必须要有该文件。
如何使用包
以上述包为例,介绍包如何使用,包内各文件代码如下:
# package/module3.py
def test3():
print('from package/module3')
# package/small_package/module1.py
def test1():
print('package/small_package/module1')
# package/small_package/module2.py
def test2():
print('package/small_package/module2')
# __init__.py文件内容均为空
包属于模块的一种,包和包内的模块都是用来被导入使用的,而不是被直接执行,导包就是导入包的__init__.py
文件,首次导入包(import package)同样会做三件事:
执行包的
__init__.py
文件;产生一个名称空间,即
__init__.py
的名称空间,用于存放__init__.py
文件执行过程中产生的名字;在当前执行文件所在的名称空间中放一个包的名字,该名字指向
__init__.py
的名称空间,例如import 包:会将包的__init__.py
文件执行一遍,得到的名字都是该__init__.py
文件中存在的名字,也就是说包里面的子包或子模块如果不在__init__.py
中,是不会被执行的,就不会被导入。
import package
package.module3.test3() # 会抛出异常AttributeError: module 'package' has no attribute 'module3'
package.module3.test3() 要求package下有名字module3,进而package.module3下有名字test,package.module3下已经有test3了,所以问题就是package下没有名字module3,这就需要在package下的__init__.py
中导入模块module3。
包的导入方式和模块相同,分为import
和from ... import ...
两种,但是无论以何种方式,无论任何位置,导入时在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。包A和包B下有同名模块也不会冲突,因为A.a与B.a来自俩个命名空间。
如何设计包
作为包的设计者,需要关注两件事:方便自己组织管理程序;给包的使用者提供便利。
首先,方便自己组织管理程序。可以将不同功能的代码放在放在不同的模块(py文件)或者不同的子包(子文件夹)中,这样可以结构清晰的管理组织自己的模块功能。
其次,如何给使用者提供使用包的便利。使用者一般都最求简单的使用方式。所以包的设计者最好将使用方式尽可能设计的简单清晰。因为使用者使用包的方式和使用模块的方式一模一样的,import ...
,或 from....import....
的方式。不论哪种方式,都会执行模块的程序代码。所以如果是包的话,就需要在__init__.py
中隐藏好导入的关系,方便使用者使用。
__init__.py
文件模块导入方式
为了方便包的使用者更加方便的使用包,在包的__init__.py
文件中可以导入包内的其他模块,分为绝对导入和相对导入。
首先来看绝对导入,绝对导入就是以顶级包作为起始位置进行导入。
# package包的__init__.py
from package.small_package.module1 import test1 # 必须从顶级包开始
import也能使用绝对导入,导入过程中同样会依次执行包下的__init__.py
,只是基于import导入的结果,使用时必须加上该一堆前缀。
import package.small_package.module1.test1
package.small_package.module1.test1()
然后来看相对导入,相对导入中.
代表当前文件所在目录,..
代表当前目录的上一级目录,以此类推。包内模块相互导入只能使用from ... import ... 不能使用import ...,语法错误,包内模块的相互导入推荐使用相对导入。
# package包的__init__.py
from .small_package.module1 import test1