Python自定义模块

到目前为止,读者已经掌握了导入 Python 标准库并使用其成员(主要是函数)的方法,接下来要解决的问题是,怎样自定义一个模块呢?

前面章节中讲过,Python 模块就是 Python 程序,任何 Python 程序都可以作为模块。也就是说,随便写的一个 Python 程序,都可以作为模块。例如,下面程序定义了一个简单的模块(编写在 module1.py 文件中):
print('这是module 1')
my_book = 'Python入门教程'
def say_hi(user):
    print('%s,您好,欢迎学习Python' % user)
class User:
    def __init__(self, name):
        self.name = name
    def walk(self):
        print('%s正在慢慢地走路' % self.name)
    def __repr__(self):
        return 'User[name=%s]' % self.name
上面程序中,第一行代码执行了一条简单的输出语句,第二行代码定义了一个 my_book 变量。接下来程序定义了一个 say_hi() 函数,然后定义了一个 User 类。不难发现,这个程序和我们前面所写的 Python 程序并没有太大的区别,它就可以作为模块。

使用模块的好处在于,如果将程序将频繁使用的代码段(比如刚刚定义的 say_hi() 函数、User 类)定义在模块中,后面不管哪个程序,只要导入该模块,此程序就可以直接使用该模块所包含的代码段,从而避免每个程序都需要重复编写这些代码段,提高了程序的可复用性。

模块文件的文件名就是它的模块名,比如 module1.py 的模块名就是 module1。

自定义模块编写测试代码

当自定义模块编写完成之后,需要编写一些测试代码,检验模块中各个功能是否都能正常运行。由于模块就是一段 Python 程序,因此只要模块中包含可执行代码,就可以直接执行模块中的程序。

比如,利用 Python IDLE 打开 module1.py 文件并运行,即可看到如下输出结果:

这是module 1

上面这行输出是因为 module1.py 程序中包含了一条 print 语句。但棋块中的变量、函数、类都没有得到测试,因此还应该为这些变量、函数、类提供测试程序。

例如,可以为上面的 module1.py 增加如下测试代码:
# ===以下部分是测试代码===
def test_my_book ():
    print(my_book)
def test_say_hi():
    say_hi('孙悟空')
    say_hi(User('Charlie'))
def test_User():
    u = User('白骨精')
    u.walk()
    print(u)
test_my_book()
test_say_hi()
test_User()
上面代码为 module1 定义了三个函数,分别用于测试模块中的变量、函数和类,如果此时再运行该模块,可以得到如下运行结果:

这是module 1
Python入门教程
孙悟空,您好,欢迎学习Python
User[name=Charlie],您好,欢迎学习Python
白骨精正在慢慢地走路
User[name=白骨精]

对于实际开发的项目,对每个函数、类可能都需要使用更多的测试用例进行测试,这样才能达到各种覆盖效果(比如语句覆盖、条件覆盖等)。本教程并不介绍测试相关内容,因此就不深入展开了。

需要注意的是,像上面这样,直接向模块程序中添加测试代码,会导致一个问题,即当其它程序导入该模块时,其中的测试函数会自动执行。例如,在 module1.py 同目录下,再创建一个新的 module2.py 文件,向该文件中添加如下代码:
import module1
运行 module2.py 文件,可以看到如下运行结果:

这是module 1
Python入门教程
孙悟空,您好,欢迎学习Python
User[name=Charlie],您好,欢迎学习Python
白骨精正在慢慢地走路
User[name=白骨精]

这显然不是期望看到的结果。我们希望的效果是:如果直接运行模块文件(相当于测试文件),程序会执行该模块的测试函数;如果是其他程序导入该模块,程序不应该执行该模块的测试函数。

要实现这个效果,需要借助 Python 内置的 __name__ 变量。当直接运行一个模块时,name 变量的值为 __main__;而将模块被导入其他程序中并运行该程序时,处于模块中的 __name__ 变量的值就变成了模块名。因此,如果希望测试函数只有在直接运行模块文件时才执行,则可在调用测试函数时增加判断,即只有当 __name__ =='__main__' 时才调用测试函数。

例如,修改 module1.py 模块文件的测试代码,如下所示:
# ===以下部分是测试代码===
if __name__=='__main__':
    def test_my_book ():
        print(my_book)
    def test_say_hi():
        say_hi('孙悟空')
        say_hi(User('Charlie'))
    def test_User():
        u = User('白骨精')
        u.walk()
        print(u)
    test_my_book()
    test_say_hi()
    test_User()
修改完成后,再次执行模块文件(module1.py),测试代码会执行;而执行导入模块文件的 module2.py,模块中的测试代码不会执行。由此,就实现了我们想要的效果。

自定义模块编写说明文档

与前面介绍的函数、类相同的是,在实际开发中往往也应该为模块编写说明文档;否则,其他开发者将不知道该模块有什么作用,以及包含哪些功能。

为模块编写说明文档很简单,只要在模块开头的位置定义一个字符串即可。例如,在上面程序的第一行代码之前添加如下内容:

'''
这是我们编写的第一个模块,该模块包含以下内容:
my_book:字符串变量
say_hi:简单的函数
User:代表用户的类
'''

这段字符串内容将会作为该模块的说明文挡,可通过模块的 __doc__ 属性来访问文档。例如,在 module2.py 中添加如下代码:
import module1
print(module1.__doc__)
运行 module2.py,执行结果为:

这是module 1

这是我们编写的第一个模块,该模块包含以下内容:
my_book:字符串变量
say_hi:简单的函数
User:代表用户的类