23 创建包package

就像模块是函数、变量、类等程序基本要素的集合,包则是模块的集合,更适合一个项目。像很多的第三方知名的模块都是以包的形式存在供大家使用,例如numpy、pandas以及tensorflow等,它们很大可能涉及上千个文件(模块),有的时候还有子目录出现(包的子包)。 下面做一个简单的例子,看看Python里是如何创建、使用包的。

23.1 简单的包实现

自己做一个ammd包,功能简单的只有加减乘除等功能,加减在一个模块matham里,乘除位于另一个模块里mathmd。 下面是matham模块的代码:

def add(x, y):
    return x + y
def minus(x, y):
    return x - y

下面是mathmd模块的代码:

def mul(x, y):
    return x * y
def div(x, y):
    return x / y

和这两个模块文件mathad.py、mathmd.py同目录(ammd)下创建一个空的__init__.py文件(此文件是ammd包区分于其他目录的一个重要标志),注意是init前后是两个_下划线。现在ammd目录下的文件有:

liao@liao:~/md/package$ tree
.
└── ammd
    ├── __init__.py
    ├── mathad.py
    └── mathmd.py

1 directory, 3 files
liao@liao:~/md/package$ 

那么ammd目录名就是包名字。

23.2 配置包的搜索路径

包创建成功以后是否可直接在其他源代码里使用包及其下的模块了呢?答案是不行的,也得配置包,让Python能够找到新写的包,这个可以参考前一章里的配置模块搜索路径配置模块搜索路径 的方法中的“方法四”配置包的搜索路径。

配置包的搜索路径(ubuntu)

编辑上一章里的.pth文件,新增ammd所在的目录,本例ammd包位于package目录下,所以本例里的包的路径应该到package。

liao@liao:~/md/package$ pwd
/home/liao/md/package
liao@liao:~/md/package$ ls
ammd
liao@liao:~/md/package$ 

/home/liao/md/package目录添加到/usr/local/lib/python2.7/dist-packages/mypkpath.pth文件里。可以用gedit软件来编辑mypkpath.pth文件,由于这个文件是非用户目录下的文件,所以得用特权帐号模式来编辑,在gedit前加sudo。

liao@liao:~$ sudo gedit /usr/local/lib/python2.7/dist-packages/mypkpath.pth 

上一章添加了模块myam的搜索路径和本章添加ammd包的搜索路径,最后mypkpath.pth文件里的内容如下:

liao@liao:~/md/package$ cd /usr/local/lib/python2.7/dist-packages/
liao@liao:/usr/local/lib/python2.7/dist-packages$ cat mypkpath.pth 
/home/liao/md/test/module
/home/liao/md/package
liao@liao:/usr/local/lib/python2.7/dist-packages$ 

配置包的搜索路径(windows)

Windows用户配置包的路径也需要在.pth文件里加入包的父目录即可。操作方法参加前一章模块在windows下的搜索路径配置部分方法四下的(1)的内容。

23.3 测试包

from ammd.mathad import add
import ammd.mathmd

print add(12, 13)
print ammd.mathmd.mul(12, 13)

程序运行结果如下:

25
156

23.4 发布包

发布一个自己编写的包,需要在包的目录下写一个setup.py文件,这也是为何我们在按第三方模块的时候会看见某模块的安装需要执行python setup.py install的原因。有关如何写setup.py文件可以参考python的wiki 有较为详尽的说明,本文就不在此聊这个问题了。

23.5 子包的使用

如果包下有子包,子包的目录下也得有一个__init__.py文件。使用子包的语法格式如下:

包名.子包名.变量名
包名.子包名.函数名
包名.子包名.类名

当然也可先用import 引入包和子包,再用子包里模块的函数、类等元素。

from 包.子包 import 函数或类等
import 包.子包 as 别名
别名.函数或类等

以python的优秀的第三方包(模块)numpy为例,numpy下还有子包random、linalg、f2py等子包,如下所示:

liao@liao:~/$ cd  /usr/lib/python2.7/dist-packages/numpy/
liao@liao:/usr/lib/python2.7/dist-packages/numpy$ ls
add_newdocs.py   __config__.pyc  distutils  fft                __init__.pyc  matlib.py   random     tests
add_newdocs.pyc  core            dual.py    _import_tools.py   lib           matlib.pyc  setup.py   version.py
compat           ctypeslib.py    dual.pyc   _import_tools.pyc  linalg        matrixlib   setup.pyc  version.pyc
__config__.py    ctypeslib.pyc   f2py       __init__.py        ma            polynomial  testing
liao@liao:/usr/lib/python2.7/dist-packages/numpy$ tree random/
random/
├── info.py
├── info.pyc
├── __init__.py
├── __init__.pyc
├── mtrand.x86_64-linux-gnu.so
├── randomkit.h
├── setup.py
├── setup.pyc
└── tests
    ├── test_random.py
    ├── test_random.pyc
    ├── test_regression.py
    └── test_regression.pyc

1 directory, 12 files
liao@liao:/usr/lib/python2.7/dist-packages/numpy$ 

在numpy的random子包目录下也有__init__.py文件。

示例:如何使用子包里的模块?

import numpy
from numpy.random import standard_normal
import numpy.random as nr
a = nr.randint(100, 200)# 先引入包的子包,代码第3行
print "a =", a
b = numpy.random.randint(10, 20)#需引入包numpy,代码第1行
print "b =",b
c = standard_normal(15) #从包.子包里引入该函数,代码第2行
print "c is:\n", x

程序执行结果:

a = 188
b = 11
c is:
[ 0.87079202 -0.87667013  0.89928634  1.52703587  1.05317002  1.30343433
 -0.32560727  1.41482243  1.72747541  0.36661236  0.98762842  0.70661896
 -0.55792225  1.50491544 -1.10518371]