



|
5.2 模块 |
模块是Python中重要的概念,Python的程序是由一个个模块组成的。前面已经接触到了模块,一个Python文件就是一个模块。下面将介绍模块的概念和特性。
模块把一组相关的函数或代码组织到一个文件中。一个文件即是一个模块。模块由代码、函数或类组成。创建一个名为myModule.py的文件,即定义了一个名为myModule的模块。在myModule模块中定义一个函数func()和一个类MyClass。MyClass类中定义一个方法myFunc()。
01 # 自定义模块
02 def func(): # 定义func函数
03 print ("MyModule.func()")
04
05 class MyClass: # 定义MyClass类
06 def myFunc(self): # 定义myFunc方法
07 print ("MyModule.MyClass.myFunc()") # 调用myFunc()方法
然后在myModule.py所在的目录下创建一个call_myModule.py的文件。在该文件中调用myModule模块的函数和类。
01 #调用自定义模块的类和函数 02 import myModule # 导入moudle 03 04 myModule.func() 05 myClass = myModule.MyClass() 06 myClass.myFunc()
【代码说明】
·第2行代码导入模块myModule。
·第4行调用模块的函数。调用时需要加前缀myModule,否则Python不知道func()所在的命名空间。输出结果:
myModule.func()
·第5行代码创建类MyClass的实例myClass。这里也需要使用前缀myModule调用类。
·第6行调用类的方法myFunc()。输出结果:
myModule.MyClass.myFunc()
注意 myModule.py和call_myModule.py必须放在同一个目录下,或放在sys.path所列出的目录下;否则,Python解释器找不到自定义的模块。
当Python导入一个模块时,Python首先查找当前路径,然后查找lib目录、site-packages目录(Python\Lib\site-packages)和环境变量PYTHONPATH设置的目录。如果导入的模块没有找到,在以上路径搜索一下是否含有这个模块。可以通过sys.path语句搜索模块的查找路径。
在使用一个模块的函数或类之前,首先要导入该模块。前面已经多次使用模块的导入,模块的导入使用import语句。模块导入语句的格式如下所示。
import module_name
这条语句可以直接导入一个模块。调用模块的函数或类时,需要以模块名作为前缀,其格式如下所示。
module_name.func()
如果不想在程序中使用前缀符,可以使用from…import…语句导入。from…import…语句的格式如下所示。
from module_name import function_name
2.2.3小节比较了import语句和from…import…语句的不同。导入模块下所有的类和函数,可以使用如下格式的import语句。
from module_name import *
此外,同一个模块文件支持多条import语句。例如,定义一个名为myModule的模块。该模块定义一个全局变量count和一个函数func()。每次调用函数func(),使变量count的值加1。
01 count = 1 # 定义全局变量count并赋值 02 03 def func(): # 定义func函数 04 global count 05 count = count + 1 06 return count
多次导入myModule模块,查看变量count的结果。
01 import myModule
02 print("count =", myModule.func())
03 myModule.count = 10
04 print ("count =", myModule.count)
05
06 import myModule
07 print ("count =", myModule.func())
【代码说明】
·第1行代码导入模块myModule。
·第2行代码调用模块中的函数func()。此时变量count的值等于2。输出结果:count=2。
·第3行代码给模块myModule中的变量count赋值,此时变量count的值等于10。
·第4行代码获取变量count的值。输出结果:count=10。
·第6行代码再次导入模块myModule,变量count的初始值为10。
·第7行代码调用func(),变量count的值加1。输出结果:count=11。
Python中的import语句比Java的import语句更灵活。Python的import语句可以置于程序中任意的位置,甚至可以放在条件语句中。在上面的代码段后添加如下语句:
01 # import置于条件语句中
02 if myModule.count > 1:
03 myModule.count = 1
04 else:
05 import myModule
06 print ("count =", myModule.count)
【代码说明】
·第2行代码判断myModule.count的值是否大于1。
·第3行代码,如果count的值大于1,则把变量count的值置为1。由于前面代码段中变量count的值为11,所以变量count的值被赋值为1。
·第5行代码,如果count的值小于等于1,则导入import语句。
·第6行代码输出变量count的值。输出结果:count=1
模块有一些内置属性,用于完成特定的任务,如__name__、__doc__。每个模块都有一个名称,例如,__name__用于判断当前模块是否是程序的入口,如果当前程序正在被使用,__name__的值为“__main__”。通常给每个模块都添加一个条件语句,用于单独测试该模块的功能。例如,创建一个模块myModule。
01 if __name__ == '__main__':
02 print ('myModule作为主程序运行')
03 else:
04 print ('myModule被另一个模块调用')
【代码说明】 第1行代码判断本模块是否作为主程序运行。单独运行模块myModule,输出结果如下所示。
myModule作为主程序运行
创建另一个模块call_myModule。这个模块很简单,只要导入模块myModule即可。
01 import myModule 02 print (__doc__)
【代码说明】 运行模块call_myModule,输出结果:
myModule被另一个模块调用
第2行代码调用了模块另一个属性__doc__。由于该模块没有定义文档字符串,所以输出结果为None。输出结果:None
Python提供了一个内联模块buildins。内联模块定义了一些开发中经常使用的函数,通过dir(buildins)可以查看内联模块中所有的内置函数,利用这些函数可以实现数据类型的转换、数据的计算、序列的处理等功能。下面将介绍内联模块中常用的函数。
Python3中移除了apply函数,所以不再可用了。调用可变参数列表的函数的功能只能使用在列表前添加*来实现。
filter()可以对某个序列做过滤处理,判断自定义函数的参数返回的结果是否为真来过滤,并一次性返回处理结果。filter()的声明如下所示。
class filter(object)
filter(function or None, iterable) --> filter object
下面这段代码演示了filter()过滤序列的功能。从给定的列表中过滤出大于0的数字。
01 def func(x): # 定义func函数 02 if x > 0: # if判断 03 return x # 返回x的值 04 05 print (filter(func, range(-9, 10))) # 调用filter函数,返回的是filter对象 06 print(list(filter(func, range(-9, 10))) # 将filter对象转换为列表
【代码说明】 第5行代码,使用range()生成待处理的列表,然后把该列表的值依次传入func()。func返回结果给filter(),最后将结果yield成一个iterable对象返回,可以进行遍历。输出结果如下。
<filter object at 0x0000019C68C88948>
直接打印出的是filter对象,无法看出其内容。第6行将其转换为列表。输出结果如下。
[1, 2, 3, 4, 5, 6, 7, 8, 9]
注意 filter()中的过滤函数func()的参数不能为空。否则,没有可以存储sequence元素的变量,func()也不能处理过滤。
对序列中元素的连续操作可以通过循环来处理。例如,对某个序列中的元素累加操作。Python提供的reduce()也可以实现连续处理的功能。在Python2中reduce()存在于全局空间中,可以直接调用。而在Python3中将其移到了functools模块中,所以使用之前需要先引入。reduce()的声明如下所示。
reduce(func, sequence[, initial]) -> value
【代码说明】
·参数func是自定义的函数,在函数func()中实现对参数sequence的连续操作。
·参数sequence待处理的序列。
·参数initial可以省略,如果initial不为空,则initial的值将首先传入func()进行计算。如果sequence为空,则对initial的值进行处理。
·reduce()的返回值是func()计算后的结果。
下面这段代码实现了对一个列表的数字进行累加的操作。
01 def sum(x, y): # 定义sum函数 02 return x + y # 返回x + y的值 03 from functools import reduce # 引入reduce 04 print (reduce(sum, range(0, 10))) 05 print (reduce(sum, range(0, 10), 10)) 06 print (reduce(sum, range(0, 0), 10))
【代码说明】
·第1行代码,定义了一个sum()函数,该函数提供两个参数,执行累加操作。
·第4行代码,对0+1+2+3+4+5+6+7+8+9执行累加计算。输出结果为45。
·第5行代码,对10+0+1+2+3+4+5+6+7+8+9执行累加计算。输出结果为55。
·第6行代码,由于range(0,0)返回空列表,所以返回结果就是10。输出结果为10。
reduce()还可以对数字进行乘法、阶乘等复杂的累计计算。
注意 如果用reduce()进行累计计算,必须在sum中定义两个参数,分别对应加法运算符两侧的操作数。
第4章使用了map()对tuple元组进行“解包”操作,调用时设置map()的第一个参数为None。map()的功能非常强大,可以对多个序列的每个元素都执行相同的操作,并返回一个map对象。map()的声明如下所示。
class map(object) map(func, *iterables) --> map object
【代码说明】
·参数func是自定义的函数,实现对序列每个元素的操作。
·参数iterables是待处理的序列,参数iterables的个数可以是多个。
·map()的返回值是对序列元素处理后的列表。
下面这段代码实现了列表中数字的幂运算。
01 def power(x): return x ** x # 定义power函数 02 print (map(power, range(1, 5))) # 打印map对象 03 print(list(map(power,range(1,5)))) # 转换为列表输出 04 def power2(x, y): return x ** y 05 print (map(power2, range(1, 5), range(5, 1, -1))) # 打印map对象 06 print(list(map(power2, range(1, 5), range(5, 1, -1)))) # 转换为列表输出
【代码说明】
·第1行代码定义了一个power()函数,实现了数字的幂运算。
·第2行代码把数字1、2、3、4依次传入函数power中,将计算结果yield成一个iterable对象,输出结果:
<map object at 0x0000020A24765148>
·第3行代码将map对象转换为列表然后打印出来,输出结果:
[1, 4, 27, 256]
·第4行代码,定义了一个power2()函数,计算x的y次幂。
·第5行代码,提供了两个列表参数。依次计算1^5、2^4、3^3、4^2,计算后的结果同样yield成一个iterable对象。输出结果:
<map object at 0x0000020A24765B48>
·第6行代码将map对象转换成为列表输出。输出结果:
[1, 16, 27, 16]
注意 如果map()中提供多个序列,则每个序列中的元素一一对应进行计算。如果每个序列的长度不相同,则短的序列后补充None,再进行计算。
常用内置函数一览表如表5-1所示。
表5-1 内置模块的函数
包就是一个至少包含__init__.py文件的文件夹。Python包和Java包的作用是相同的,都是为了实现程序的重用,把实现一个常用功能的代码组合到一个包中,调用包提供的服务从而实现重用。例如,定义一个包parent。在parent包中创建两个子包pack和pack2。pack包中定义一个模块myModule,pack2包中定义一个模块myModule2。最后在包parent中定义一个模块main,调用子包pack和pack2,如图5-2所示。
图5-2 包与模块的树形关系图
【例5-1】通过代码完成自定义包。包pack的__init__.py程序如下所示。
【例5-1.py】
01 if __name__ == '__main__': # 判断本模块是否作为主程序运行
02 print ('作为主程序运行')
03 else:
04 print ('pack初始化')
这段代码初始化pack包,这里直接输出一段字符串。当pack包被其他模块调用时,将输出“pack初始化”。包pack的myModule模块如下所示。
01 def func(): # 定义func函数
02 print ("pack.myModule.func()")
03
04 if __name__ == '__main__': # 判断本模块是否作为主程序运行
05 print ('myModule作为主程序运行')
06 else:
07 print ('myModule被另一个模块调用')
当pack2包被其他模块调用时,将首先执行__init__.py文件。pack2包的__init__.py程序如下。
01 if __name__ == '__main__':
02 print ('作为主程序运行')
03 else:
04 print ('pack2初始化')
包pack2的myModule2模块如下。
01 def func2(): # 定义func2函数
02 print ("pack2.myModule2.func()")
03
04 if __name__ == '__main__': # 判断本模块是否作为主程序运行
05 print ('myModule2作为主程序运行')
06 else:
07 print ('myModule2被另一个模块调用')
下面的main模块调用了pack、pack2包中的函数。
01 from pack import myModule 02 from pack2 import myModule2 03 04 myModule.func() 05 myModule2.func2()
【代码说明】
·第1行代码从pack包中导入myModule模块,myModule模块被main模块调用,因此输出字符串“myModule被另一个模块调用”。输出结果如下。
pack初始化 myModule被另一个模块调用
·第2行代码从pack2包中导入myModule2模块。输出结果如下。
pack2初始化 myModule2被另一个模块调用
·第4行代码调用myModule模块的函数func()。输出结果如下。
pack.myModule.func()
·第5行代码调用myModule2模块的函数func2()。输出结果如下。
pack2.myModule2.func()
__init__.py也可以用于提供当前包的模块列表。例如,在pack包的__init__.py文件前面添加一行代码。
__all__ = ["myModule"]
__all__用于记录当前pack包所包含的模块。其中方括号中的内容是模块名的列表,如果模块数量超过2两个,使用逗号分开。同理,在pack2包也添加一行类似的代码。
__all__ = ["myModule2"]
这样就可以在main模块中一次导入pack、pack2包中所有的模块。修改后的main模块如下。
01 from pack import * 02 from pack2 import * 03 04 myModule.func() 05 myModule2.func2()
【代码说明】
·第1行代码,首先执行pack包的__init__.py文件,然后在__all__属性中查找pack包含有的模块。如果pack包的__init__.py文件不使用__all__属性记录模块名,main模块调用时将不能识别myModule模块。Python将提示如下错误。
NameError: name 'myModule' is not defined
·第2行代码与第1行代码的作用相同。