By ZevieZ
目录
简介
我们经常能够在python的源程序开头看到类似下面的语句
import sys
import numpy as np
它们代表我们引用了这两个模块的内容,类似c++的头文件,我们可以在后续程序中调用这些模块的函数,引用它们内置的常量等等。
那么,我们如何编写并引用自己的模块呢?笔者将对此详细展开并提供实现源代码和文件结构以供参考。
1. Python模块的编写
在python程序开头import sth 其实可以理解为将sth.py的文件与改程序建立了一个接口,通过后续简单地访问sth.funcname(文件名+函数名)或是 sth.constname(文件名+常量名)就可以访问sth.py文件中的定义的函数和常量等。
所以说,所谓模块,其实本质上就是一个python文件。我们如果想在某个程序中调用自己编写的模块,只需先自己编写一个包含目标我们想要的目标功能的python文件,然后在该源程序中调用即可。
而编写模块,其实也就是跟平常一样进行常规的代码的书写而已。值得注意的是,函数名的定义是由模式规范的,如果在函数名前面加了’_’下划线,那么在后续的调用时会自动忽略这个函数。
现在来看看我编写的一个模块案例吧。
首先我们构造一个文件名为tool.py的python源文件:
'''
ZevieZ实现的tool工具包
'''
#debug模式,提示debug信息
DEBUG = 0
#错误提示
ERROR_HINT = 0
def show_module_func():
'''
展示模块功能
'''
print("工具函数包,包括向量计算工具等")
return 0
def vector2_add(v1,v2):
'''
计算两个二维向量相加,返回一个新的列表v
'''
v = [0,0]
Len1 = len(v1)
Len2 = len(v2)
if ERROR_HINT:
if Len1 != Len2:
print("向量的长度不同")
return None
elif Len1 != 2:
print("向量的长度不合法,应为2")
return None
for i in range(Len1):
v[i] = v1[i] + v2[i]
return v
pass
def vector3_add(v1,v2):
'''
计算两个三维向量相加,返回一个新的列表v
'''
v = [0,0,0]
Len1 = len(v1)
Len2 = len(v2)
if ERROR_HINT:
if Len1 != Len2:
print("向量的长度不同")
return None
elif Len1 != 3:
print("向量的长度不合法,应为3")
return None
for i in range(Len1):
v[i] = v1[i] + v2[i]
return v
pass
if __name__ == '__main__':
print("-----调用的工具包部分----")
v1 = [0,0,1]
v2 = [0,1,0]
v3 = vector3_add(v1,v2)
此模块能够实现基础的向量相加功能。
需要注意的是,如果不对sys.path进行任何修改,那么想要后续在主程序种导入该模块,这个tool.py模块文件必须和主程序处于同一文件目录下。否则是不能以import tool的形式导入的。
2. 模块的导入
模块的导入有几种不同的方式。
1)首先,最简单的方式是导入一个模块的接口。对于上面我所构建的模块,为:
import tool |
以这种方式导入模块时,不运行这些代码。后续需要在程序中以“点式模块名”的形式访问并调用该模块内部的函数、类或者变量等。
如:
tool. vector3_add([1,2,3],[3,2,1]) |
此为以“点式模块名”的形式访问并调用该模块内部的函数实现向量相加的简单例子。
2)如果只是运用到模块中的某一种子模块功能,例如我只想用到vector2_add二维向量相加功能。不想每次都以tool.vector2_add()这种麻烦的方式访问。可以在开始的import部分进行如下的操作:
from tool import vector2_add |
这样一来,在程序之后的调用中,只需直接引用函数名称,而非“模块.函数名”的形式进行函数的调用。
3)导入模块的全部子模块。
from tool import* |
这是一种最简单且无脑的方式,能够一次性导入该模块中的全部内容。但是假如用户调用了好几种不同的模块,如果都用这种方式,很有可能引用有重名函数早成函数的冲突,所以对于复杂的工程项目,一般并不推荐这种方案。当然,如果您的程序非常简单,其实也无所谓啦。
4)以别名方式导入模块
import tool as tol |
如果以这种形式导入我们的模块,在之后的调用中,便可以用tol代替tool,效果时完全相同的(当然,是在不和其它名称定义存在冲突的情况下)。
3. 以脚本方式运行模块
我们知道,其实所谓的模块本质就是一段脚本,那么模块当然可以作为脚本运行。但是这样的话问题就来了,我们如何能够让模块既有作为脚本运行的功能,又让其作为模块被导入时不被运行呢?
其实很简单。Python源文件有一个环境变量__name__,只有其作为脚本运行时,__name__才会等于字符串常量’__main__’。利用这一点,我们可以区分模块的应用情景。
if __name__ == '__main__':
print("-----调用的工具包部分----")
v1 = [0,0,1]
v2 = [0,1,0]
v3 = vector3_add(v1,v2)
'''
print("-----调用的工具包部分----")
v1 = [0,0,1]
v2 = [0,1,0]
print("向量v1: ",v1)
print("向量v2: ",v2)
v3 = vector3_add(v1,v2)
print("v3 = v1 + v2: ")
print("向量v3: ",v3)
'''
上面不被注释的部分,只有当其作为脚本运行时才会生效,其他情形不会被运作。而注释中的部分不一样,无论何时都会运行。这就是为什么我们常常在别人的程序种看到形如:
if __name__ == '__main__':
'''
code
'''
pass
的语句,其实这样做就是为了做一个调用模块和运行脚本的区分。
4. 查看模块定义的名称
利用python内置函数dir(),可以查找模块定义的名称,返回结果是经过排序的字符串列表。
如果对我编写的tool模块调用此函数,结果为:
print(dir(tool),'\n') |
5. 完整的应用实例
此为一个完整的引用实例,感兴趣的读者可以运行看看。运行结果包含详细的解释。
import sys
import tool
def main():
print("-------模块搜索路径------\n")
print(sys.path)
print("-------运行的主程序------\n")
print("tool工具包信息:")
print(dir(tool),'\n')
v1 = [0,1,3]
v2 = [1,4,2]
print("向量v1: ",v1)
print("向量v2: ",v2)
v3 = tool.vector3_add(v1,v2)
print("v3 = v1 + v2: ")
print("向量v3: ",v3)
return 0
if __name__ == '__main__':
main()
可以通过sys.path访问模块的默认访问地址,sys.path为一个列表,将你构建的模块的地址加入这个列表,便可以在导入模块时不协商冗长的地址。
tool即为本文第一节中构建的模块文件,可以看到,在主程序中,只需简单地利用import tool 导入即可。
运行结果如下:
后续会更新如何开发并引用自己设计的python包的相关内容,感兴趣的读者可以下载链接中的资源,运行代码进行实战练习。