Python算法设计与分析从入门到精通
上QQ阅读APP看书,第一时间看更新

3.5 函数

提到函数,大家首先会想到数学函数。数学中,函数是非常重要的知识点,贯穿整个数学的学习过程。Python中,函数的应用同样非常广泛,地位也非常重要。

前面我们已经多次接触过函数,如用于输出的print()函数、用于输入的input()函数,以及用于生成一系列整数的range()函数等。这些都是Python内置的标准函数,可以直接使用。除了这些标准库函数外,Python还支持用户自定义函数,即用户可将一段有规律、重复的代码定义为函数,实现一次编写、多次调用的目的。使用函数可以提高代码的重复利用率。

3.5.1 函数的定义

创建函数的过程就是定义函数的过程,Python中使用def关键字来实现。具体的语法格式如下:

def functionname([parameterlist]):
   ['''comments''']
   [functionbody]

参数说明:

 functionname:函数名称,在调用函数时使用。

 parameterlist:可选参数,用于指定向函数中传递的参数。如果有多个参数,各参数间使用逗号“,”分隔。如果不指定,表示该函数没有参数,调用时同样不用指定参数。

 '''comments''':可选参数,表示为函数指定注释,注释的内容通常是说明该函数的功能、要传递的参数的作用等,可以为用户提供友好提示和帮助的内容。

 functionbody:可选参数,用于指定函数体,即该函数被调用后要执行的功能代码。如果函数有返回值,可以使用return语句返回。

注意

① 即使函数没有参数,也必须保留一对空的小括号“()”,否则将提示如图3.47所示的错误。

② 函数体“functionbody”和注释'''comments'''相对于def关键字,必须保持一定的缩进。

图3.47 语法错误对话框

说明

定义函数时如果指定了'''comments'''参数,调用函数时输入函数名称及左侧的小括号,将显示该函数的帮助信息,如图3.48所示。这些帮助信息是通过定义的注释提供的。如果没有显示友好提示,需要检查函数是否有误,检查方法如下:调用该方法前,先按F5键执行一遍代码。

图3.48 调用函数时显示友好提示

例如,定义一个过滤危险字符的函数filterchar(),代码如下:

运行上面的代码,将不显示任何内容,也不会抛出异常,因为filterchar()函数还没有被调用。

说明

如果想定义一个什么也不做的空函数,可以使用pass语句作为占位符。

3.5.2 函数的调用

调用函数的过程就是执行函数的过程。如果把创建函数理解为创建一个具有某种用途的工具,那么调用函数就相当于使用该工具。调用函数的基本语法格式如下:

functionname([parametersvalue])

参数说明:

 functionname:要调用的函数名称。必须是已经创建好的函数。

 parametersvalue:可选参数,用于指定各个参数的值。如果需要传递多个参数值,则各参数值间使用逗号“,”分隔。如果该函数没有参数,则直接写一对小括号即可。

例如,调用在3.5.1节创建的filterchar()函数,可以使用下面的代码:

01  about = '我是一名程序员,喜欢看黑客方面的图书,想研究一下Trojan。'
02  filterchar(about)

调用filterchar()函数后,将显示如图3.49所示的结果。

图3.49 调用filterchar()函数的结果

3.5.3 函数参数的传递

调用函数时,多数情况下主调函数和被调函数之间存在一定的数据传递关系,这种数据传递是通过函数参数来实现的。函数参数包括形式参数和实际参数两种,下面来详细讲解。

1.形式参数和实际参数

定义函数时,函数名后面括号中的参数称为形式参数,简称形参。

如图3.50所示,这里person、height和weight是fun_bmi()函数的3个形参,这里并没有给出具体的数值,而只是简单对参数数量、含义进行了说明。

图3.50 函数形参

调用函数时,函数名后面括号中的参数称为实际参数,简称实参。简而言之,实参就是函数调用者提供给函数参与实际运算的参数,有着具体的值或意义。例如,函数调用语句“fun_bmi("路人甲",1.83, 60)”中,“路人甲”、1.83和60就是给出的3个需要参与实际运算的参数,这就是实参。

形参和实参的关系可以通过图3.51更好地理解。

图3.51 形式参数与实际参数

【实例3.18】 输出某个人的BMI指数。(实例位置:资源包\Code\03\18)

在IDLE中创建function_bmi.py文件,然后在该文件中创建一个名称为function_tips的函数,该函数包含3个参数,分别用于指定姓名、身高和体重,再根据公式“BMI =体重/ (身高×身高)”,计算人的BMI指数,并输出计算结果。

具体代码如下:

运行结果如图3.52所示。

从该实例代码和运行结果可以看出:

(1)定义一个根据身高、体重计算BMI指数的函数fun_bmi(),在定义函数时指定的变量person、height和weight称为形式参数。

(2)在函数fun_bmi()中根据形式参数的值计算BMI指数,并输出相应的信息。

(3)调用fun_bmi()函数时,指定的“路人甲”、1.83和60都是实际参数,函数执行时这些值将被传递给对应的形式参数。

图3.52 调用函数输出励志文字

2.值传递和引用传递

函数调用时,实参将传递数据给形参,传递的可能是实参的值,也可能是实参的引用。具体来说,当实参为不可变对象时,进行的是值传递;当实参为可变对象时,进行的是引用传递。

值传递和引用传递的基本区别在于:进行值传递后,改变形参的值,实参的值不受影响;进行引用传递后,改变形参的值,实参的值也将一同被改变。

例如,定义一个名称为demo的函数,然后为demo()函数传递一个字符串类型的变量作为参数(代表值传递),并在函数调用前后分别输出该字符串变量;再为demo()函数传递一个列表类型的变量作为参数(代表引用传递),并在函数调用前后分别输出该列表。代码如下:

上面代码的执行结果如下:

从运行结果可以看出,进行值传递时,改变形参的值后,实参的值未发生改变;进行引用传递时,改变形参的值后,实参的值也发生了改变。

3.位置参数

在函数定义(或调用)阶段,按照从左到右的顺序定义(或调用)的参数,称为位置参数。位置参数又称为必备参数,凡是按位置定义的形参都必须被传值,多一个不行,少一个也不行,否则将产生错误。

1)数量必须与定义时一致

调用函数时,指定的实参数量必须与形参数量一致,否则将抛出TypeError异常,提示缺少必要的位置参数。例如,调用实例3.18中编写的BMI指数函数fun_bmi(person,height,weight),将参数少传一个,即只传递两个参数,代码如下:

fun_bmi("路人甲",1.83)            #计算路人甲的BMI指数

函数调用后,将显示如图3.53的错误提示。抛出的异常类型为TypeError,具体提示信息为“fun_bmi()方法缺少一个必要的位置参数weight”。

2)位置必须与定义时一致

在调用函数时,指定的实参位置必须与形参位置一致,否则将抛出TypeError异常或得到错误结果。

图3.53 缺少必要的参数时抛出的异常

(1)抛出TypeError异常。调用函数时,当实参类型与形参类型不一致,且两种类型间无法自动转换时,将抛出TypeError异常。例如,调用实例3.18中编写的fun_bmi(person,height,weight)函数,将第1个参数和第2个参数位置调换,代码如下:

fun_bmi(60,"路人甲",1.83)            #计算路人甲的BMI指数

函数调用后,将显示如图3.54所示的异常信息。这是因为传递的整型数值不能与字符串进行连接操作。

图3.54 提示不支持的操作数类型

(2)产生的结果与预期不符。调用函数时,如果实参与形参位置不一致,但数据类型一致,则不会抛出异常,而是产生与预期不符的结果。例如,仍然调用fun_bmi(person,height,weight)函数,将第2个参数和第3个参数位置调换,代码如下:

fun_bmi("路人甲",60,1.83)           #计算路人甲的BMI指数

函数调用后,结果如图3.55所示。这里虽然未抛出异常,但得到的结果并不是我们想要的。

图3.55 结果与预期不符

说明

当传递的实参位置与形参位置不一致时,并不总会抛出异常,所以在调用函数时一定要确定好位置,否则容易产生Bug,而且很不容易发现。

4.关键字参数

关键字参数是指使用形参的名字来确定输入的参数值。通过该方式指定实参时,不再需要与形参的位置完全一致。只要将参数名写正确即可。这样可以避免用户需要牢记参数位置的麻烦,使得函数的调用和参数传递更加灵活方便。

例如,调用fun_bmi(person,height,weight)函数时,通过关键字参数指定各个实际参数,代码如下:

fun_bmi( height = 1.83, weight = 60, person = "路人甲")  #计算路人甲的BMI指数

函数调用后,将显示以下结果:

路人甲的身高:1.83米 体重:60千克
路人甲的BMI指数为:17.916330735465376
您的体重过轻 ~@_@~

从结果可以看出,虽然指定的实参顺序与函数定义中不一致,但运行结果与预期是一致的。