第5章函数
在程序设计过程中,程序员常会遇到这样的情况,有些操作经常重复出现,有时是在一个程序中多次重复出现,有时是在不同程序中多次重复出现,这些重复运算的程序段是相同的,只不过是每次都以不同的参数进行重复。如果在一个程序中,相同的程序段多次重复编写,势必会使程序行数过多,一方面会占用大量的存储空间,另一方面又浪费宝贵的编程时间。对于许多都要使用的计算函数和求解问题的程序,情况也是如此。如果每个需要这些程序的人都自行单独设计,将浪费大量的时间与**,大大地降低程序员的工作效率。解决这一问题的有效办法就是将上述各种情况下共同使用的程序设计成可供其他程序使用的独立程序段。
在Python中,这样的程序段被称为函数,其实质就是计算思维中的模块设计概念。对于数值���算、数据库管理、界面布局、多线程、游戏设计等领域的问题,也可以使用函数这一技术。例如,已知正整数m和n,要求计算组合值m!/n!/(m-n)!。在这个应用程序中,要使用计算阶乘的程序3次,每次就只对不同的数据进行计算,而程序结构则是一样的。
在Python中,函数分为内置函数、标准库函数、第三方库函数和自定义函数四大类。本章主要介绍自定义函数及其调用,内容包括嵌套调用、返回列表、形式参数与实在参数、全局变量与局部变量、lambda函数、递归函数等。
5.1函数定义与调用
在Python编程规范中,程序书写的顺序通常是,首先进行函数定义,然后才是通过主程序实现函数调用。
5.1.1函数定义与调用
Python除了提供大量丰富的内置函数和模块方法,还允许自定义函数,这为编写程序提供了一种有效扩展功能的方法。
1. 函数定义
用户在定义函数时需要使用def语句,在def语句内部一方面是定义函数的功能,另一方面是将函数值返回给主调函数(即主程序)。
def语句的一般引用格式如下: def <函数名>(<形参表>):
<函数体>
return <返回值>说明:
(1) 定义函数由保留字def开头,且与其后的<函数名>之间要用空格分隔;
(2) <函数名>必须是合法的Python标识符,且其后必须跟圆括号以标识函数;
(3) 圆括号后必须跟冒号,从而让系统自行识别并处理后续的<函数体>;
(4) <形参表>位于括号内,表示函数涉及的参数,由逗号分隔不同的形参;
(5) <函数体>用于实现函数的具体执行功能;
(6) <函数体>由语句块组成,必须缩进固定空格数,且*好沿用IDLE交互环境中的隐含设置;
(7) 若是有参函数,则要有<返回值>选项,可由return语句实现。
2. 函数调用及其返回
(1) 函数调用。调用函数的一般格式如下: <函数名>(<实参表>)说明: <实参表>表示函数调用时所需的全部参数。由于实参中的值将传递给形参,所以实参应该是常数、有值的变量、表达式、另一个函数调用等。
(2) 返回语句。return语句的功能是从被调函数返回到主调函数继续执行,返回时可以附带一个返回值,由return语句后面的参数指定。其一般引用格式如下: return<表达式>说明:
① <表达式>用于表示函数的返回值。
② 定义函数必须以return(<表达式>)语句结尾,除非没有返回值。
③ 若有可选项<表达式>,则函数将返回该表达式的值;若无可选项<表达式>),则系统自动返回常量None。图51函数调用过程
一个函数可以通过return语句返回一个特定类型的值,也可以不使用return语句,而是只执行函数主体中的代码,这种情况下,函数将向调用者返回一个未定义的值。实际上,创建函数的方法与编写其他应用程序是完全相同的,其差别仅仅是在一个函数中至少要有一条return语句,除非没有返回值。
(3) 函数调用过程。当函数调用出现在主程序中时,将执行所指定的a()函数。在a()函数执行到return语句时,将流程转移主程序,同时将返回值传递给主程序,然后继续执行函数调用后面的语句。函数调用的过程如图51所示。
3. 无参函数及其调用
无参函数在没有参数的情况下,使函数没有通用性,即运行结果是固定的。一般用于显示指定信息,若涉及数据也只是转换,而不是计算,所以没有返回值。
【例51】显示3行提示信息。
源程序如下: #定义两个无参函数
def printstar():
print("")
def print_message():
print(" Computation Thinking! ")
#主程序并3次调用无参函数
printstar()
print_message()
printstar()本例中定义两个无参函数,主程序将三次调用无参函数。
运行结果如图52所示。
图52例51的运行结果
【例52】显示一个平行四边形图案。
源程序如下: #定义无参函数
def printstar():
s=""
for i in range(0,7,1):
#显示左侧空格
for k in range(0,i+3,1):
print(" ",end="")
print(s)#显示星号串
#主程序并调用无参函数
printstar()本例中定义无参函数只是显示图案,所以主程序直接调用,并没有参数传递和变量引用。
运行结果如图53所示。
图53例52的运行结果
4. 有参函数及其调用
【例53】找出两个数中的较大数。
源程序如下: #定义有参函数
def larger(x,y):
if x>y:
return x
else:
return y
#主程序及函数调用
a=int(input("输入第1个数: "))
b=int(input("输入第2个数: "))
c=larger(a,b)
print("较大数: \t",c)本例中的有参函数larger()在获得由实参a和b传递的数据后,使函数能够找出指定两数中的较大数,从而使函数具有通用性,即运行结果不是固定的。
运行结果如图54所示。
图54例53的运行结果
5.1.2嵌套调用
在执行主程序时可以调用**个函数,在执行**个函数时还可以调用第二个函数,这样可以一个又一个地调用下去,这种调用称之为嵌套调用。下面用一个例子加以说明。
【例54】计算1+(1+2)+…+(1+2+3+4+5+6+7+8+9+10)。
求解方法: 由于这里的和式由20项阶加组成,但每一项又是一个需若干数据阶加的式子。所以要定义两个函数,并进行嵌套调用。
源程序如下: #定义函数sum1()对一项阶加
def sum1(n):
sum=0
for i in range(1,n+1,1) : sum=sum+i
return sum
#定义函数sum2()对若干数据累加
def sum2(n):
sum=0
for i in range(1,n+1,1):
#第二次调用函数
sum=sum+sum1(i)
return sum
#主程序并嵌套调用函数
n=int(input("输入数据: "))
#**次调用函数
s=sum2(n)
print("累加和值: ",s)本例中的嵌套调用过程是,主程序调用sum2()函数,sum2()函数再调用sum1()函数,从而构成两重嵌套的调用函数过程。
运行结果如图55所示。
图55例54的运行结果
5.1.3返回值类型与函数类型
由于Python使用解释方式实现语义分析,进而导致变量的数据类型可以动态变化。所以,实参类型、返回值类型和函数类型均可以动态变化。
【例55】实参类型动态变化示例。
源程序如下: #定义函数
def larger(x,y):
if x>y:
return x
else:
return y
#主程序及其4次调用函数
a=3.5
b=4.5
c=larger(a,b)
print(a,b,"中的较大数: ",c)
m=6
n=2
d=larger(m,n)
print(m,n,"中的较大数: ",d)
s1="This"
s2="That"
e=larger(s1,s2)
print(s1,s2,"中的较大数: ",e)
c1=3+4J
c2=4-5J
f=larger(c1,c2)
print(c1,c2,"中的较大数: ",f)本例中进行4次函数调用,第1次是2个实数作为实参,第2次是2个整数作为实参,第3次是2个字符串作为实参,第4次是2个复数作为实参。运行结果表明,前3次调用都得到正确结果,但是复数不能进行大于比较,因此系统将显示错误代码。
运行结果如图56所示。
图56例55的运行结果
5.1.4返回列表
让函数返回一个列表,并由列表中的多个元素来描述函数的多值计算,能够解决函数式程序设计中只能实现单值返回的问题。
【例56】编程生成由6个随机数构成的列表。
源程序如下: #导入random模块
import random
#定义函数生成由n个随机数构成的列表
def my_random(n):
a=[]
for k in range(n):
#将一个随机数附加到列表中
a.append(random.random())
return a
#主程序及其调用函数
n=int(input("输入数据: "))
m=my_random(n)
print("列表元素: ")
#输出列表中的所有元素
for k in m: print(k)运行结果如图57所示。
图57例56的运行结果
5.2形式参数与实在参数
Python的函数与其他**语言一样,主程序把一些参数传递给函数,在函数对这些参数进行运算和处理后,再把结果传递给主程序。参数传递是通过主程序中的实在参数和被调用函数中的形式参数相结合来完成的,换句话说,在主程序中要列出实在参数,而在函数中要设置一些形式参数。当进行函数调用时,使形式参数和实在参数相结合,从而实现参数的传递。形式参数(简称形参)和实在参数(简称实参)相结合的具体情况如下:
(1) 实参和形参在数量上*好相等;
(2) 对应位置上的实参和形参*好数据类型一致或兼容;
(3) 实参可以是常量、变量、表达式或另一个函数,而形参只能是变量;
(4) 如果实参为常量,则将常量赋值给形参;
(5) 如果实参为表达式,则将表达式的值赋值给形参;
(6) 如果实参为另一个函数,则一定要能够获得返回值;
(7) 如果实参为已赋值变量,则将其值赋给形参。
5.2.1简单变量作为实参
【例57】设定两个正整数m和n,计算组合值m!/n!/(m-n)!。
求解方法: 本例将定义计算阶乘的函数,其中实参是有值的简单变量,其值由系统自行赋给形参,主程序通过3次调用计算阶乘的函数来获得组合值。
源程序如下: #定义有参函数comp()
def comp(k):
fact=1
for i in range(1,k+1,1) : fact=facti
return fact
#主程序
m=int(input("m="))
n=int(input("n="))
#三次调用函数comp()
c=comp(m)/comp(n)/comp(m-n)
print("6!/2!/4!=",c)运行结果如图58所示。
图58例57的运行结果
【例58】利用穷举算法找出两个正整数中的*大公约数。
求解方法: 获取两数中的较小数并由变量least表示,使用for循环穷举可能的因数,即变量k的取值范围(必须降序)为least~1。若x、y同时能够整除k,则表示k是*大公约数,将其值返回给主程序。由于k的取值过程是从大到小的,所以**条件成立时就是*大公约数。
源程序如下: #定义函数gcd(x, y)找出两数中的*大公约数
def gcd(x,y):
#获取两数中的较小数
if x>y:
least=y
else:
least=x
for k in range(least,0,-1):
#若x, y同时整除k, 则k是*大公约数
if(x % k==0 and y % k==0):
gcd=k
return gcd
#主程序
a=int(input("a="))
b=int(input("b="))
#调用函数并显示结果
print(a,"和",b,"的*大公约数为",gcd(a,b))运行结果如图59所示。
图59例58的运行结果
【例59】利用欧几里得算法找出两个正整数中的*大公约数。
求解方法: 欧几里得算法又称辗转相除法。设定两个正整数为x和y,若x%y的值大于0,则重复进行如下3个操作: r=x%y,x=y,y=r,直到x%y为0时,这时的y就是*大公约数。
源程序如下: #定义函数gcd(x, y)找出两数中的*大公约数
def gcd(x,y):
r=x%y
while r>0:
x=y
y=r
r=x%y
return y
#主程序及其调用函数
a=int(input("a="))
b=int(input("b="))
print(a,"和",b,"的*大公约数为",gcd(a,b))【例510】验证哥德巴赫猜想。猜想的大意是,任意大于等于6的偶数都可以写成两个质数之和,例如: 6=3+3,8=3+5,10=3+7。编程验证100以内的所有偶数。
求解方法: 定义p(m)函数,功能是若m为质数,则函数返回值为1,否则返回值为0。主程序使用for 循环穷举所有偶数,并两次调用p(m)函数进行是否质数的判断。另外,使用变量count控制每行显示5个偶数。由于至今人类并没有发现反例,所以验证过程能够正常进行。
源程序如下: #定义函数p(m), 若取值为1则是质数, 否则取值为0表示不是质数
def p(m):
k=2
flag=0
while kif m%k==0:#不是质数
flag=1
break
k=k+1
if flag==0:
return 1
else:
return 0
#主程序及其两次调用函数
count=0#控制换行
for n in range(6,100,2):#穷举全部偶数
for k in range(3,n,1):#穷举可能的质数
if p(k)==1 and p(n-k)==1:
count=count+1
print(n,"=",k,"+",n-k,end="\t")
if count%4==0 : print()
break运行结果如图510所示。
图510例510的运行结果
【例511】找出100以内两两相邻的全部质数,例如3和5,5和7,11和13,等等。
源程序如下: #定义函数
def p(m):
k=2
flag=0
while kif m%k==0:
flag=1
break
k=k+1
if flag==0:
return 1
else:
return 0
#主程序及其两次调用函数
count=0
for n in range(3,100,2):#穷举全部奇数
if p(n)==1 and p(n+2)==1:#是否是相邻质数
count=count+1
print("第",count,"对相邻质数: ",n,":",n+2)主程序使用for 循环穷举所有奇数,并两次调用p(n)和p(n+2)函数进行是否质数的判断。
运行结果如图511所示。
图511例511的运行结果
【例512】找出500以内的全部亲密数对。
如果整数A的全部因数(包括1,不包括A本身)之和等于B,且整数B的全部因数(包括1,不包括B本身)之和等于A,则将整数A和B称为亲密数对。例如220和284就是一对亲密数,计算过程如下:
(1) 220 =1+ 2+ 4+ 5+ 10+ 11+ 20+ 22+ 44+ 55+ 110= 284
(2) 284 =1+ 2+ 4+ 71+ 142= 220
源程序如下: #定义函数p()求n的因数和
def p(n):
s=0
for k in range(1,n,1):
if n%k==0 : s=s+k
return s
#定义函数display()显示n的因数阶加式
def display(n):
print(n,"=1",end="")
for k in range(2,n,1):
if n%k==0 : print("+",k,end="")
#主程序及其两次调用函数
for a in range(1,500,1):
for b in range(a+1,500,1):
if p(a)==b and p(b)==a:#测试是否亲密数对
display(a)
print("=",b)
display(b)
print("=",a)
print("亲密数: ",a,b)
break