1.4 函数
函数是指用于实现一个特定功能并能重复使用的代码段。前面学到的input和print这些函数是Python语言的内建函数,可以直接使用。也可以自己定义函数来实现我们想要的功能,这样的函数叫作用户自定义函数。本节就来了解用户自定义函数。
1.4.1 定义子函数
要想使用函数,必须先定义它。
定义函数的格式为:
def函数名(变量名):
其中,变量名指传进子函数的数据在子函数内部对应的变量名,可以没有,也可以有一个或者多个。函数名是在调用函数时要使用的名字,两个函数不能有相同的变量名。也就是说,在Python中已经存在print函数,所以在创建自定义函数时不能以print作为变量名,但是可以使用Print作为变量名(一个字符大写了)。
最后,如果这是一个有返回值的函数,要在函数的最后写上“return返回值”。
长篇大论不如一个例子,这就来创建一个函数看看。
01 def Plus(a,b):
02 print(a+b)
03
04 def Hello():
05 num1=int(input("Hello! Please enter the first number:"))
06 num2=int(input("Please enter the second number:"))
07 return num1,num2
08
09 n1,n2=Hello()
10 Plus(n1,n2)
这是一个用于计算两个数的和的程序。第一个函数Plus用于计算两个数的和,第二个函数Hello用于向用户问好并输入两个数字。
Plus函数被调用时(第10行),需要提供两个值。a对应n1的值,b对应n2的值,在函数内部使用这两个值时只能调用a和b,不能调用n1和n2。如果调用语句是Plus(3,5),那么a==3、b==5,a和b继承它们对应位置上的值。这个函数没有返回值,只需要print,所以在函数结尾不需要写return。
从 Hello 函数的定义语句中可以看到,括号内并没有变量名,这代表调用函数时不需要提供任何值。在函数内,num1和num2存储了两个输入的值,用return返回。Python和其他语言不同,可以同时返回多个值,而且可以是不同类型的值。返回多个值时,在等号的左边用多个变量接住,变量之间用逗号隔开;同样,变量继承它们对应位置上的值。在程序中,n1继承了num1的值,n2继承了num2的值。
注意:由于程序是从上到下运行(行数编号从小到大)的,所以函数必须在调用语句的上面定义。也就是说,程序第9行的代码如果被换到第3行,程序就会报错,因为这时Hello函数还没有被定义。另外,和if、while和for语句相似,函数的定义语句下的代码段也要有缩进。
在这段程序中,做加法的简单任务不创建函数其实更简单。在复杂的程序中,使用自定义函数可以使解决问题的过程简便直观得多。
主函数的定义是程序的入口函数。其他的函数(不是主函数的函数叫作子函数)和方法都在主函数中调用。
1.4.2 主函数
在C语言中,主函数和子函数之外的语句只能是定义语句。而在Python中,函数之外也可以有定义语句之外的语句,比如输出等。Python程序运行时只会运行不在函数内的代码和主函数内的代码段,不会运行子函数内的代码段。子函数内的代码段只有在被调用时才会运行。
因为有这个特性,如果程序的调用和被调用范围仅限于本文件内,可以不编写主函数,直接在所有子函数下面编写代码就可以。但是,为了养成好的习惯和直观性,建议在编写程序时加上主函数。本书为了简单易懂,有些代码段不使用主函数。
下面我们来了解一下在Python中如何定义主函数。
01 if __name__=='__main__':
02 print("Inside")
运行程序,输出结果为:
Inside
if__name__=='__main__'是定义主函数的语句。定义函数的时候要注意代码块前面的缩进(本质上是if语句的格式)。
再来看一个例子:
01 def sq(num):
02 print(num*num)
03 print("Outside")
04 if __name__=='__main__':
05 print("Inside")
运行程序,输出结果为:
Outside
Inside
在函数外和主函数内的输出语句都被执行了,而子函数sq因为没有被调用,其中的输出语句并没有被执行。
1.4.3 调用函数
在1.4.1节介绍子函数时,就在程序中调用了我们定义的子函数。但是,不同的子函数有不同的调用方法。本节就来讲解调用各种函数的规律。
在定义子函数时,还要在函数的括号中定义好需要传进子函数的变量。调用函数的格式和为函数定义的需要传进函数的变量有关。调用函数时传进的数据是什么类型的变量,函数内部对应位置上的变量就会是同样类型的。
比如,我们定义了一个函数 calc,需要三个变量,最后返回的值是第一个变量和第二个变量的乘积减去第三个数的值,如图1.29所示。
图1.29 函数calc
函数定义和调用时的格式是这样的:
01 def calc(a,b,c):
02 ans=a*b-c
03 return ans
04 ans=calc(2,3,4)
05 print(ans)
运行程序,输出结果:
2
如果在调用函数时写的是calc('2',3,4),那么函数就会在执行的时候出错,因为'2'对应的是a,3对应的是b,4对应的是c,string类型的变量无法和int类型的变量进行计算。
值得注意的是,在调用函数时,我们还使用了一个变量来存储函数的返回值。需不需要这个变量取决于函数的内容。如果函数有返回值,也就是有 return 语句的话,就需要用一个变量存储这个返回值;反之,如果没有返回值,那么就可以直接调用,不需要变量来存储返回值。
如果想要输出这个返回的值,或者再使用它进行其他一次性操作,也可以不专门用一个变量存储,而直接把这个函数写入进行操作的语句中。再拿calc函数举例说明:
01 def calc(a,b,c):
02 ans=a*b-c
03 return ans
04 print(calc(2,3,4))
运行程序,输出结果为:
2
这样,就达到了简化程序的目的。
再来举一个没有返回值的函数的例子:
01 def Output(strArr):
02 for i in range(10):
03 print(strArr[i])
04 Arr=['Apple','Bear','Cat','Dog','Egg','Fruit','Giraffe','Hat','Ice','Jacket']
05 Output(Arr)
运行程序,输出结果为:
Apple
Bear
Cat
Dog
Egg
Fruit
Giraffe
Hat
Ice
Jacket
1.4.4 全局变量
在之前见到的程序中,子函数内部的变量和子函数外部的变量完全不相关,所以不必担心重名的问题;子函数外的语句无法调用子函数内部的变量,子函数内部的语句也无法调用子函数外部的变量。这使程序的结构更加清晰,但也带来了一些不便:难道想使用外部的数据就必须要传入子函数中吗?
全局变量,顾名思义,就是可以让整个程序使用的变量。它一般在整个程序的最开始定义,在所有的函数和语句的上方。如图1.30所示,局部变量只在函数的小范围内存在,跳出这个范围,局部变量不存在。而全局变量在整个程序的范围内存在。
图1.30 全局变量与局部变量
再次用calc函数举例说明:
01 g=10
02 def calc(a,b,c):
03 ans=a*b-c
04 return ans
05 print(calc(2,3,4))
在这段程序中,g 就是一个全局变量。在主函数和自己定义的子函数之外的所有语句中,都可以像调用一个平常变量一般调用它。但是,在子函数内部时,就要给它多加一条语句:
01 global g
global 的意思是“全球的”,在程序中也可以被理解为“全局的”。在子函数内,在变量名前面加上global这条语句用于声明这是一个全局变量。有了这条语句,这个子函数内的所有代码都可以像各个子函数外的代码一样调用这个变量并对它进行更改,而且更改的结果也会在全局范围内保存。与调用函数时传进函数的变量不同,在函数内更改这些非全局变量并不会影响它们本来的值。
1.4.5 函数的运用
我们来看一个问题:1个苹果2元,1个菠萝8元,1个西瓜10元。1个盒子1元,它最多能装下5个苹果或1个菠萝或1个西瓜。编写一个程序,让用户输入每种水果的购买数量,然后输出每种水果加上盒子需要多少钱,以及一共要多少钱。
先自己思考一下该怎么做,再来看下面的代码。
首先是没有使用函数的解决方法:
然后是使用了函数的解决方法:
现在,你熟悉子函数的定义和使用方法了吗?
根据图1.31所示的关系来写一个满足问题要求的程序。
图1.31 各科分数评价
首先,允许用户输入数学、英语、历史这三科的考试分数,每一科的分数范围在0~100之间,超出这个范围的分数是错误分数,需要重新输入。第二,对每一科的分数做出评价,评价标准如图1.31所示。
第三,按表1.2中规定的比例折算各科分数并得到总分,总分范围是0~300。
表1.2 各科折算总分的比例
续表
第四,按照表1.3中的标准,给总分一个等级。最后,输出对每一门课考试成绩的评价、折算完的总分数和总分对应的等级。分别用5个子函数实现这5个要求。
表1.3 总分对应等级
这是一个比较复杂的题目,提供的数据和条件比较多。先自己思考该怎么做,用纸和笔记录下自己的思路和计划,再动手去写代码。写完代码后,再对照书上的答案验证。记住,不同的代码也可以实现相似的功能,书上的答案仅供参考。
那么,首先来写出这个程序的框架:
先定义好所有要使用的子函数和主函数。语句next是用来“跳过”一个子函数的。当子函数内部没有任何语句时,Python就会把下一个子函数或者主函数当作子函数内部的语句,从而因为缩进不对而报错,加上next语句就可以有效地防止这种情况的发生。同时,根据子函数的功能来判断它们需要什么值来完成任务,从而确定括号内的变量。
现在,就把这些子函数填满。
到这里,整个程序就完成了。
看完这两个问题之后可以发现,如果没有注释的话,没有使用函数的解决方法虽然也不难完成,但是在写完一大段连着的代码之后很容易混淆代码的用处。而使用了函数就可以有效避免这种情况的发生。所以,使用函数还可以让程序变得更有条理。
到这里,第1章就结束了。在接下来要学习的诸多算法中,将会大量使用本章讲解的语法。在进入下一章之前,一定要先熟悉这些语法,可以多做题目来练习。如果你觉得还没有弄懂,那就请重新读一读本章再继续接下来的学习吧!