22 创建自己的模块

模块,通常是一个含有源代码的文件,文件里有设计好了的函数、类、变量或者类的对象等内容。模块的作用是有效地避免多源代码文件的大项目时函数名、变量名、类名等程序基本要素的名字冲突问题,在一个多文件的大工程的不同模块文件里可以在不同的文件里出现相同的变量名、函数名、类名等。那一个工程如何区分不同模块里的变量名、类名、函数名呢?语法格式如下:

模块名.变量名
模块名.函数名
模块名.类名

即使用点运算(.),每个包含源代码的文件名(不含.py)做为模块名。引用非重名的变量、函数、类等也是这样通过点运算来引用的。

举例说明,list列表和tuple元组都有index函数,都是Python内建模块和函数,日常使用时是直接通过该类型的变量(本质是类的对象)点运算调用各自类(后续章节会讲类的知识)的index函数,其实也可以通过list.index或者tuple.index的方式来使用。

a = range(10)
print a
print a.index(3)
print list.index(a, 3)
t = tuple(a)
print t
print t.index(3)
print tuple.index(t, 3)

程序执行结果:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3
3
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
3
3

22.1 自己模块

在理解了Python的模块的意义、用途和使用方式之后,可以考虑自己如何编写模块、使用和配置模块等问题。

下面以编写求整数加、减法模块为例讲解模块的创建和基本使用。模块文件myam.py源代码如下所示:

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

另为编写一个使用myam模块(注意模块名不含.py)的程序testam.py,请将模块文件myam.py和使用模块的文件testam.py暂时放在同一个目录下,如下所示:

liao@liao:~/md/module$ tree
.
├── myam.py
├── myam.pyc
└── testam.py

0 directories, 3 files
liao@liao:~/md/module$ 

使用模块myam的testam.py的代码如下所示:

import myam
print myam.add(13, 10)
print myam.minus(13, 10)

由于在非模块文件里引入某模块的方式很多,所以testam.py的编写方式很多,例如:

from myam import add, minus
print add(13, 10)
print minus(13, 10)

程序执行完毕后请看一下模块所在目录下是否多了一个文件?是的多了一个和模块文件同名的.pyc文件,对这个文件是在第一次使用模块时Python自动编译生成,删除了模块源文件.py留下模块的.pyc文件,使用模块的testam.py同样能正常运行,故模块开发好以后可以不提供源文件只提供.pyc文件,算是一种自我的知识产权的保护?呵呵。

22.2 配置模块搜索路径

在前一节,为了展示模块的开发和使用,暂时将模块文件和使用模块的文件放置在了同一个目录下,问题就来了,如果模块文件和使用模块的文件不在同一目录下会怎样?大家可以做一个简单的实验,将使用模块的文件放在另一个目录下,运行程序看会怎样?

liao@liao:~/md$ tree test
test
├── module
│   ├── myam.py
│   └── myam.pyc
└── testam.py
1 directory, 3 files
liao@liao:~/md$

是的程序运行时会报错找不到模块,平时使用的其他模块时例如csv、random也没要求这些模块和使用程序必须在同一个目录下啊,显然这里会有一些问题,怎么解决?

实际上无论是标准模块还是自己写的模块,使用模块的程序是按照某种规则(实际是一些目录)去寻找模块,按照所有已知规则如果找不到才会报错模块找不到,解决办法就是让Python能找到新写的模块即可。

有几种方式可以让Python找到新写的这个模块。

方式一:新模块拷贝到标准目录下

第一种就是把新写的模块的.py拷贝一份放在规则可以找到的地方(目录),这样做很简单,但缺点是会产生垃圾,系统移植性不好,故建议这么去做。

方式二:修改sys.path环境变量

第二种方式是使用模块的文件通过修改sys模块path变量(列表),添加新建模块的保存路径,这种方式不会产生系统垃圾,但移植到另外一台计算机上修要根据实际修改模块存在的路径,修要调整程序,移植性差点儿。

import sys
dirm = "/home/liao/md/test/module"
sys.path.append(dirm)
print sys.path
import myam
print myam.add(12, 15)

当程序和模块移植到其他计算机上需要修改模块所在目录dirm。

方式三:修改PYTHONPATH环境变量

第三种方式是修改系统的环境变量,Linux用户需要修改PYTHONPATH,shell里执行:

liao@liao:~/md/test/module$ export PYTHONPATH=/home/liao/md/test/module:$PYTHONPATH
liao@liao:~/md/test/module$ cd ..
liao@liao:~/md/test$ python testam.py 
23
3

但是问题来了,这只是针对一个模块,如果一台计算机机上要运行很多的自己写的模块,那么这个PYTHONPATH变量修要写很多的目录,移植性差。而且在shell里修改PYTHONPATH只是在当前Shell里有用,重新开一个Shell就不起作用了.

liao@liao:~$ cd md
liao@liao:~/md$ cd test/
liao@liao:~/md/test$ python testam.py 
Traceback (most recent call last):
  File "testam.py", line 1, in <module>
    import myam
ImportError: No module named myam
liao@liao:~/md/test$ echo $PYTHONPATH

liao@liao:~/md/test$ 

解决办法是在~/.bashrc里写一条export PYTHONPATH=/home/liao/md/test/module:$PYTHONPATH后每次启动shell就能找到myam模块了,尽管解决了一点问题那也移PYTHONPATH,如果windows下系统变量没有,新建,把模块目录作为该变量的值value,这可参考windows的Path环境变量windows的Path环境变量 来做一做。

方式四:.pth文件

第四种,也是个人觉得最好的方式,将模块的目录统一管理,存放在一个.pth文件里.pth文件里

(1)Windows用户可以在c:\python27或者c:\python27\site-packages目录下新建一个.pth文件,例如mypkpath.pth,在这个文件里添加模块所在目录。

(2) ubuntu用户,在/usr/local/lib/python2.7/dist-packages目录下创建.pth文件,在.pth文件里记录新建模块的路径。

(3)redhat用户,在/usr/lib/python2.7/site-packages目录下创建.pth文件,在.pth文件里记录新建模块的路径。

liao@liao:~/md/test$ cd /usr/local/lib/python2.7/dist-packages
liao@liao:/usr/local/lib/python2.7/dist-packages$ sudo touch mypkpath.pth
[sudo] liao 的密码: 
liao@liao:/usr/local/lib/python2.7/dist-packages$ ls *.pth
mypkpath.pth
liao@liao:/usr/local/lib/python2.7/dist-packages$ 

将模块目录添加到该文件里:

liao@liao:/usr/local/lib/python2.7/dist-packages$ cat mypkpath.pth 
/home/liao/md/test/module
liao@liao:/usr/local/lib/python2.7/dist-packages$ cd 
liao@liao:~$ cd md/
liao@liao:~/md$ cd test/
liao@liao:~/md/test$ ls
module  testam.py
liao@liao:~/md/test$ python testam.py 
23
3
liao@liao:~/md/test$ 

方式四的操作方式最好,值得推荐!