从零开始学Python数据分析与挖掘
上QQ阅读APP看书,第一时间看更新

3.4 自定义函数

3.4.1 自定义函数语法

虽然Python的标准库中自带了很多“方法”或函数,并且第三方模块也提供了更多的现成“方法”与函数,但有时还是不能满足学习或工作中的需求,这时就需要自定义函数了。另外,为了避免重复代码的编写,也可以将常用的代码块封装为函数,在需要时调用函数即可,这样也会使代码简洁易读。

在Python中有一种自定义函数叫匿名函数,可以用lambda关键字定义。通过lambda构造的函数可以没有名称,最大特点是“一气呵成”,即在自定义匿名函数时,所有代码只能在一行内完成,语法如下:

    lambda parameters : function_expression

如上语法中,lambda为匿名函数的关键起始词;parameters是函数可能涉及的形参,如果有多个参数,需要用英文状态的逗号隔开;function_expression为具体的函数体。需要再次强调的是,如果需要构造的函数不是很复杂,可以使用lambda匿名函数一气呵成地表达完,否则就只能使用def关键字构造有名称的自定义函数了。下面举一个实例来描述lambda匿名函数的使用:

本案例的目的是统计列表中的元素频次,并根据频次从高到低排序。首先在统计元素频次时使用了for循环,其中set函数是构造集合对象,可以实现列表元素的去重;然后直接对存储键值对的列表直接排序,发现默认是按照字母排序,见第三行输出,并不是以实际的频次排序;最后通过构建匿名函数,对列表元素(每一个键值对元组)的第二个元素降序排序,进而实现输出结果中的最后一行效果。

虽然匿名函数用起来很灵活,会在很多代码中遇到,但是它的最大特点也是它的短板,即无法通过lambda函数构造一个多行而复杂的函数。为了弥补其缺陷,Python提供了另一个关键字def构造复杂的自定义函数,其语法如下:

    def function_name(parameters):
         function_expression
         return(result)

如上语法中,def是define单词的缩写,表示自定义;function_name为自定义的函数名称;parameters为自定义函数的形参,需要放在圆括号内;第一行的结束必须要加上英文状态的冒号,这是很多初学者容易忽略的细节;function_expression是具体的函数体(注意,第二行开始需要缩进),根据自定义的需求,可以很简单也可以很复杂;return用于返回函数的计算结果,如果有多个值需要返回,可以全部写在return的括号内,并以逗号隔开。首先,编写一段猜数字游戏的自定义函数,用于说明自定义函数的语法:

如上的猜数字游戏代码,大家可能见过,这里在《Python简明教程》的基础上做了一定的修改,进而可以更加“智能”地告知参与游戏的用户可以在什么范围内猜数。代码中用到的知识点都是前面介绍过的基础内容,这里就不对代码做详细解释了。

3.4.2 自定义函数的几种参数

通过构造自定义函数,可以避免冗余代码的出现。关于Python中的自定义函数,还有四类重要的参数需要跟读者一一解释,即必选参数、默认参数、可变参数和关键字参数。

1.必选参数

必选参数,顾名思义就是当你在调用一个自定义函数时必须给函数中的必选参数赋值,否则程序将会报错,并提醒用户“缺少一些必选的位置参数”。就以上面的猜数字函数为例,如果不给该函数的max参数传递一个值,结果就是这样的:

如上所示,返回“类型错误”的提示,再具体查看最后一行的反馈信息,结论为“game函数缺少一个必要的位置参数max”表明game函数需要给max参数传值。

2.默认参数

默认参数是指在构造自定义函数的时候就已经给某些参数赋予了各自的初值,当调用函数时,这样的参数可以不用传值。例如计算1到n的p次方和:

如上构造的自定义函数中,n为必选参数,p为默认参数。根据结果显示,在第一次调用函数时,并没有给p参数传递任何值,函数正常运行,而且默认计算平方和;在第二次调用函数时,给p传递了新值3,此时p参数由原来的2换成了3,进而可以计算立方和。

3.可变参数

上面讲解的必选参数和默认参数都是在已知这个自定义函数需要多少个形参的情况下构建的,如果不确定该给自定义函数传入多少个参数值时,该如何自定义函数呢?这么说可能有点抽象,接下来通过对比的例子来说明。

例如,小明的弟弟小亮刚读一年级,老师布置了一些关于两个数的求和运算,针对这个问题,我们可以构建如下的自定义函数:

如果只是两个数求和的问题可以很简单地利用自定义函数add解决,但如果不是两个数之和,而是三个数或四个数或五个数之和,也就是说不确定接下来会计算几个数的和,这时再使用上面的add函数似乎就不合理了。好在Python给自定义函数提供了可变参数,目的就是解决这类问题。举例如下:

如上自定义函数中,参数args前面加了一个星号*,这样的参数就称为可变参数,该参数是可以接纳任意多个实参的。之所以能够接纳任意多个实参,是因为该类型的参数将这些输入的实参进行了捆绑,并且组装到元组中,正如输出结果中的第一行和第三行,就是自定义函数中print(args)语句的效果。

4.关键字参数

虽然一个可变参数可以接受多个实参,但是这些实参都被捆绑为元组了,而且无法将具体的实参指定给具体的形参,那有没有一种参数既可以接受多个实参,又可以把多个实参指定给各自的实参名呢?答案是关键字参数,而且这种参数会把带参数名的参数值组装到一个字典中,键就是具体的实参名,值就是传入的参数值。为了帮助读者理解关键字参数的含义,下面举一个例子来解释关键字参数。

例如某电商平台,在用户注册时,用户的手机号及出生日期为必填项,其他信息为选填项。对于选填项来说,电商平台并不知道用户会不会填,以及可能填多少个信息,而且这些信息都是有对应含义的。为了搜集信息,可以创建一个含关键字参数的自定义函数:

如上结果所示,在自定义函数info_collection中,tel和birthday都是必选参数,kwargs为关键字参数。当调用函数时,tel和birthday两个参数必须要传入对应的值,而其他的参数都是用户任意填写的,并且关键字参数会把这些任意填写的信息组装为字典,如输出中的第一行信息;为了把必选参数的值和关键字参数的值都汇总起来,在自定义函数时初设了空字典user_info,并通过字典元素增加的方法完成用户信息的搜集,如输出的第二个结果。