Python 函数文件与模块.docx
《Python 函数文件与模块.docx》由会员分享,可在线阅读,更多相关《Python 函数文件与模块.docx(8页珍藏版)》请在冰豆网上搜索。
Python函数文件与模块
Python函数、文件与模块
“探索Python”这一系列的前几篇文章已为Python编程新手介绍了几方面的主题,包括变量、容器对象和复合语句。
本文以这些概念为基础,构造一个完整的Python程序。
引入了Python函数和模块,并展示了构建一个Python程序、将其存储在文件中以及通过命令行运行该程序的方法。
返璞归真
许多流行的玩具都以这样一个概念为基础:
简单的积木。
这些简单的积木可通过多种方式组合在一起构造出全新的作品——有时甚至完全令人出乎意料。
这一概念同样适用于现实生活中的建筑领域,将基本原材料组合在一起,形成有用的建筑物。
平凡无奇的材料、技术和工具简化了新建筑物的建造过程,同样也简化了对新踏入此领域的人员的培训。
相同的基本概念也适用于计算机程序开发技术,包括以Python编程语言编写的程序。
本文介绍了使用Python创建基本构件(buildingblock)的方法,可用于解决更为复杂的问题。
这些基本构件可能小而简单,也可能庞大而复杂。
无论采用哪种形式,我们这场游戏的目的就是定义基本构件,然后使用它们来创建专属于您的杰作。
函数:
封装逻辑
在本系列的前几篇文章中,您通常不得不重复输入所有代码,即便它与上一行代码完全相同。
此要求的惟一特例就是变量的使用:
一旦初始化了变量的内容,之后就可以随时重用。
显而易见,这一用法的普及对我们大有好处。
描述杰出程序员的最流行的箴言之一就是他们很懒惰。
这并不表示杰出的程序员不努力工作——而是说他们喜欢灵活的工作方法,除非绝对必要,否则从不反复做任何相同的事情。
这也就意味着在您需要编写代码之前,首先考虑如何实现重用。
Python中有多种可实现重用的途径,但最简单的技术莫过于使用函数,也称为方法或子例程。
与绝大多数现代编程语言类似,Python支持使用方法将一组语句封装在一起,从而可在必要时重复使用。
清单1给出了一段简单的伪代码,为您展示如何在Python中编写方法。
清单1.定义函数的伪代码
defmyFunction(optionalinputdata):
initializeanylocaldata
actualstatementsthatdothework
optionallyreturnanyresults
如您所见,在Python中,函数的基本组成部分是包装器代码,指明将被重用的一系列Python语句。
函数可接受输入参数,输入参数在紧接着函数名(在本例中为myFunction)之后的圆括号内提供。
函数还可返回值(更为正式的说法是:
对象),包括像tuple这样的Python容器。
在真正着手构建函数之前,让我们先来看看关于伪代码的一些简单却重要的要点:
请注意函数名中所用的字符大小写:
大多数字符都是小写的,但在使用多个单词连接成一个函数名时,后接的各单词首字母应大写(例如,myFunction中的F)。
这就是所谓的驼峰式大小写风格(camelcasing),是Python和其他编程语言中广泛采用的一种技术,可使函数的名称更易阅读。
函数定义中的程序语句采用了缩进式排版,函数体由Python语句块构成,它们也必须像循环或条件语句那样缩进。
函数定义的第一行也称为方法签名(methodsignature),以def开头(def是define这个单词的缩写)。
方法签名以冒号结尾,表示下面的代码行是函数体。
至此,您或许已认可了使用方法的好处。
那么让我们投入进去,开始编写函数吧。
“DiscoverPython,Part6:
ProgramminginPython,Forthefunofit”中使用了一个for循环来创建乘法表。
清单2展示了同样的概念,但本例中创建的是一个函数,用于封装乘法表计算背后的逻辑。
清单2.第一个函数
>>>deftimesTable():
... forrowinrange(1,6):
... forcolinrange(1,6):
... print"%3d"%(row*col),
... print
...
>>>timesTable()
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
>>>t=timesTable
>>>type(t)
>>>t
>>>t()
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
timesTable函数定义起来非常简单,它不接受任何输入参数,也不返回任何结果。
函数体几乎与“DiscoverPython,Part6”中的语句完全相同(但该文章中的乘法表为从1到10)。
为了调用方法,并使其发挥作用,只需输入函数名后接圆括号即可。
本例中还输出了乘法表。
在Python中,函数是一类对象,与整型变量和容器对象相同。
因而,您可以将函数指派给一个变量(切记,在Python中变量是动态类型化的)。
在清单2中,我们将timesTable函数指派给变量t。
接下来的两行代码表示变量t确实指向函数。
最后,我们使用变量t调用timesTable函数。
函数:
动态更改逻辑
清单2中的timesTable函数不复杂,但也不是特别有用。
更有用的示例允许您指定用于生成乘法表的行数和列数——换言之,允许您在调用函数时动态地更改函数的操作方式。
在函数定义中使用两个输入参数即可实现这一功能,如清单3所示。
清单3.更好的乘法表函数
>>>deftimesTable2(nrows=5,ncols=5):
... forrowinrange(1,nrows+1):
... forcolsinrange(1,ncols+1):
... print"%3d"%(row*cols),
... print
...
>>>timesTable2(4,6)
1 2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
>>>timesTable2()
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
>>>timesTable2(ncols=3)
1 2 3
2 4 6
3 6 9
4 8 12
5 10 15
两个乘法表函数的定义非常相近,但清单3中的函数有用得多(通过清单3中的3次调用即可看出这一点)。
为函数添加此附加功能的方法非常简单:
提供名为nrows和ncols的两个输入参数,允许在调用函数时更改乘法表的大小。
这两个参数随后会被提供给生成乘法表的两个for循环。
关于timesTable2函数的另一要点就是两个输入参数有默认值。
在函数签名中为参数提供默认值,方法是在参数名后添加等号和值,例如nrows=5。
默认参数使程序获得了更高的灵活性,因为在您调用函数时,可以包含两个输入参数,也可以仅包含一个输入参数,甚至可以一个参数都不包含。
但这种方法可能会导致某些问题。
如果您在函数调用期间未指定全部参数,则必须显式地写出您所指定的参数的名称,以使Python解释器能够正确地调用函数。
最后一个函数调用正体现了这一点,它显式地调用了带有ncols=3的timesTable2函数,函数创建了一个5行(默认值)3列(所提供的值)的乘法表。
函数:
返回数据
使用方法时,人们最希望获得的结果并非总是乘法表。
您可能希望完成一次计算,并将计算结果值返回给调用代码。
有时要实现这两个目的,需要分别调用不返回任何数据的调用方法(子例程)和返回值的方法(函数)。
但在Python中,您无需担心这些语义问题,因为通过使用return语句,几乎可以相同的方式实现这两个目的(参见清单4)。
清单4.在函数中返回一个值
>>>defstats(data):
... sum=0.0
... forvalueindata:
... sum+=value
... return(sum/len(data))
...
>>>stats([1,2,3,4,5]) #Findthemeanvaluefromalist
3.0
>>>stats((1,2,3,4,5)) #Findthemeanvaluefromatuple
3.0
>>>stats()
Traceback(mostrecentcalllast):
File"",line1,in?
TypeError:
stats()takesexactly1argument(0given)
>>>stats("12345")
Traceback(mostrecentcalllast):
File"",line1,in?
File"",line4,instats
TypeError:
unsupportedoperandtype(s)for+=:
'float'and'str'
这个简单的函数遍历data(假设data为一个容纳有数字数据的Python容器),计算一组数据的平均值,然后返回值。
函数定义接受一个输入参数。
平均值通过return语句传回。
当您调用带有包含数字1到5的list或tuple的函数时,返回值会显示在屏幕上。
如果调用不带任何参数的函数、带非容器数据类型的函数或带内含非数字数据的容器的函数,就会导致出错。
(在此类情况下抛出错误是很有意义的。
更高级的处理方法应包含恰当的错误检查和处理,以应对这些情况,但这不在本文讨论范围内。
)
此示例已经非常有用,但还可使它更强大,如清单5所示。
在Python中,函数可返回任何有效的对象类型,包括容器类型在内。
因此,您可以计算多个数量,并轻松地将多个结果返回给调用语句。
清单5.返回复合值
>>>defstats(data):
... sum=0.0
... forvalueindata:
... sum+=value
... mean=sum/len(data)
... sum=0.0
... forvalueindata:
... sum+=(value-mean)**2
... variance=sum/(len(data)-1)
... return(mean,variance)
...
>>>stats([1,2,3,4,5])
(3.0,2.5)
>>>(m,v)=stats([1,2,3,4,5,6,7,8,9])
>>>printm,v
5.07.5
为了从一个函数中返回多个值,要将其括在一个括号中并以逗号分隔——换句话说,创建并返回一个tuple。
新stats函数的函数体要略加修改,以计算数字序列的样本方差。
最后,正如stats函数的两次调用所示,tuple值可作为一个tuple存取,也可将其解包为各自的分量。
模块:
简化代码重用
至此,您或许已相信了代码重用的价值。
但即便是使用函数,您依然需要在打算使用函数时重新输入函数体。
例如,当您打开一个新的Python解释器时,必须键入之前所创建的所有函数。
幸运的是,您可以使用模块将相关函数(和其他Python对象)封装在一起,将其保存在一个文件中,然后将这些已定义好的函数导入到新Python代码内,包含于Python解释器之中。
为介绍在Python中使用模块的方法,我们将重用清单5中的stats方法。
有两个选择:
您可以从与本文相关的压缩文件中提取名为test.py的文件,也可以在编辑器中键入函数,然后将文件保存为test.py。
完成上一步后,在您保存test.py的目录中启动一个新的Python解释器,然后输入如清单6所示的语句。
清单6.使用模块
>>>importtest
>>>test.stats([1,2,3,4,5,6,7,8,9])
(5.0,7.5)
>>>fromtestimportstats
>>>(m,v)=stats([1,2,3,4,5,6,7,8,9])
>>>printm,v
5.07.5
第一行importtest打开文件test.py并处理文件中的各条语句。
这里仅定义了stats函数,但若需要,您还可定义更多的函数。
调用stats函数时,应以模块名test作为函数前缀。
之所以使用这种复杂的名称,是出于作用域方面的考虑,作用域表示一个程序内名称的有效范围。
为告知Python您要调用的是哪个stats方法,就必须提供完整的名称。
这一点非常重要,因为您可能拥有多个名称相同的对象。
作用域规则可帮助Python判断您想使用的对象。
第三行fromtestimportstats也打开了文件test.py,但它隐式地将stats方法置入当前文件的作用域内,以使您能够直接调用stats函数(无需使用模块名)。
明智地使用from...import...语法可使您的程序更简洁,但过度的使用也会导致混淆,甚至出现更糟糕的作用域冲突错误。
不要滥用您的新武器!
模块库
使用Python编程语言的一个主要好处就是大型的内置式标准库,可作为Python模块访问。
常用模块示例如下:
math包含有用的数学函数。
sys包含用于与Python解释器交互的数据和方法。
array包含数组数据类型和相关函数。
datetime包含有用的日期和时间处理函数。
由于这些都是内置模块,因此您可以通过帮助解释器来了解更多相关内容,如清单7所示。
清单7.获得关于math模块的帮助信息>>>help(math)
Traceback(mostrecentcalllast):
File"",line1,in?
NameError:
name'math'isnotdefined
>>>importmath #Needtoimportmathmoduleinordertouseit
>>>help(math)
Helponmodulemath:
NAME
math
FILE
/System/Library/Frameworks/Python.framework/Versions/2.4/lib/
python2.4/lib-dynload/math.so
DESCRIPTION
Thismoduleisalwaysavailable. Itprovidesaccesstothe
mathematicalfunctionsdefinedbytheCstandard.
FUNCTIONS
acos(...)
acos(x)
Returnthearccosine(measuredinradians)ofx.
asin(...)
asin(x)
Returnthearcsine(measuredinradians)ofx.
math模块的帮助输出展示了所支持的大量数学函数,包括sqrt函数在内。
您可以利用此函数将您的样本方差计算转换为样本标准差计算,如清单8所示。
清单8.使用多个模块
>>>frommathimportsqrt
>>>fromtestimportstats
>>>(m,v)=stats([1,2,3,4,5,6,7,8,9])
>>>printm,sqrt(v)
5.02.73861278753
如您所见,您可以将多个模块导入到一个Python程序中。
在大型、内置的模块库与更大量的公用库(其中许多都是开放源码的)的共同协助下,您很快也会成为一名懒惰——也就是杰出——的程序员。
可执行文件
导入一个模块时,Python解释器会处理模块文件内的各行。
实际上,您可以调用Python解释器使其仅处理包含于一个文件中的一个Python程序。
在基于UNIX®的操作系统中,您可以轻松创建可执行的文件,如清单9所示。
清单9.一个完整的Python程序
#!
/usr/bin/envpython
defstats(data):
sum=0.0
forvalueindata:
sum+=value
mean=sum/len(data)
sum=0
forvalueindata:
sum+=(value-mean)**2
variance=sum/(len(data)-1)
return(mean,variance)
(m,v)=stats([1,2,3,4,5,6,7,8,9])
print"Themeanandvarianceofthevalues"
"from1to9inclusiveare",m,v
观察上例,您应该会产生几分好感,将Python程序置于文件内,并使其运行是如此简单。
本例与test.py文件中的代码之间惟一的差异就是包含了第一行。
在基于UNIX的操作系统中,本行会使Python解释器自动启动,并在终止前处理文件中的语句。
本示例中的其他行定义了stats函数、调用了函数,并输出了结果。
要运行本文件中的语句,您需要启动一个Python解释器,并让它去读取和处理文件的内容。
为实现这一目的,您必须首先将清单9中的示例输入到一个名为mystats.py的文件中,也可从与本文相关的压缩文件中提取文件。
进入包含此文件的目录,然后按清单10中所示命令执行。
注意对于Microsoft®Windows®操作系统而言,仅应使用第一条命令;其他命令是供UNIX系统(如Linux®或MacOSX)使用的。
清单10.执行Python程序
rb%pythonmystats.py
Themeanandvarianceofthevaluesfrom1to9inclusiveare5.07.5
rb%chmod+xmystats.py
rb%./mystats.py
Themeanandvarianceofthevaluesfrom1to9inclusiveare5.07.5
清单10中的命令展示了运行一个包含于文件之中的Python程序的方法。
第一条命令以文件名调用Python解释器,无论使用哪种系统安装Python、Python解释器位于哪个目录下,这种方法都有效。
第二条命令chmod使包含Python程序的文件成为可执行文件。
第三条命令告诉操作系统运行程序。
这是通过使用env程序实现的,这是一种独立于操作系统的技术,用于定位和运行程序——本例中是Python解释器。
重用与缩减
本文介绍了如何在Python中编写可重用代码。
讨论了如何在Python程序中使用方法或可重用块。
方法可接受输入参数,也可返回数据,包括容器数据类型在内。
这种功能使方法成为一种可处理大量问题的强大途径。
本文还介绍了模块,模块可使您将相关方法及数据合并入一个有组织的层次结构中,而此结构可方便地在其他Python程序中重用。
最后还介绍了如何将所有这些内容组合在一起以创建一个功能完整、独立的Python程序。
您已经看到,代码的重用也就意味着您的工作量缩减。
对于程序员而言,懒惰是一种优势而非陋习。