python学习(三):函数进阶
python学习(三):函数进阶
本篇博客主要介绍了
- 函数参数的使用
- 高阶函数介绍
- 高阶函数使用
内容整理和修改自骆昊老师的github_python100天教学。
一、函数参数
函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持函数的重载,因为我们在定义一个函数的时候可以让它有多种不同的使用方式。
1.1 参数默认
在函数定义的时候我们可以给参数提供一个默认值,这样我们在不提供给定参数的时候可以使用默认参数进行函数运算。
1 |
|
注意:带默认值的参数必须放在不带默认值的参数之后,否则将产生
SyntaxError
错误,错误消息是:non-default argument follows default argument
,翻译成中文的意思是“没有默认值的参数放在了带默认值的参数后面”。
1.2 可变参数
接下来,我们还可以实现一个对任意多个数求和的add
函数,因为Python语言中的函数可以通过星号表达式语法来支持可变参数。所谓可变参数指的是在调用函数时,可以向函数传入0
个或任意多个参数。将来我们以团队协作的方式开发商业项目时,很有可能要设计函数给其他人使用,但有的时候我们并不知道函数的调用者会向该函数传入多少个参数,这个时候可变参数就可以派上用场。下面的代码演示了用可变参数实现对任意多个数求和的add
函数。
1 |
|
1.3 关键字参数
下面是一个判断传入的三条边长能否构成三角形的函数,在调用函数传入参数时,我们可以指定参数名,也可以不指定参数名,代码如下所示。
1 |
|
在没有特殊处理的情况下,函数的参数都是位置参数,也就意味着传入参数的时候对号入座即可,如上面代码的第7行所示,传入的参数值1
、2
、3
会依次赋值给参数a
、b
、c
。当然,也可以通过参数名=参数值
的方式传入函数所需的参数,因为指定了参数名,传入参数的顺序可以进行调整,如上面代码的第9行和第11行所示。
1.3.1 命名关键字参数
调用函数时,如果希望函数的调用者必须以参数名=参数值
的方式传参,可以用命名关键字参数(keyword-only argument)取代位置参数。所谓命名关键字参数,是在函数的参数列表中,写在*
之后的参数,代码如下所示。
1 |
|
注意:上面的
is_triangle
函数,参数列表中的*
是一个分隔符,*
前面的参数都是位置参数,而*
后面的参数就是命名关键字参数。
1.3.2 可变参数和关键字参数
我们之前讲过在函数的参数列表中可以使用可变参数*args
来接收任意数量的参数,但是我们需要看看,*args
是否能够接收带参数名的参数。
1 |
|
执行上面的代码会引发TypeError
错误,错误消息为calc() got an unexpected keyword argument 'a'
,由此可见,*args
并不能处理带参数名的参数。我们在设计函数时,如果既不知道调用者会传入的参数个数,也不知道调用者会不会指定参数名,那么同时使用可变参数和关键字参数。关键字参数会将传入的带参数名的参数组装成一个字典,参数名就是字典中键值对的键,而参数值就是字典中键值对的值,代码如下所示。
1 |
|
提示:不带参数名的参数(位置参数)必须出现在带参数名的参数(关键字参数)之前,否则将会引发异常。例如,执行
calc(1, 2, c=3, d=4, 5)
将会引发SyntaxError
错误,错误消息为positional argument follows keyword argument
,翻译成中文意思是“位置参数出现在关键字参数之后”。
二、高阶函数介绍
在面向对象的世界中,一切皆为对象,所以类和函数也是对象。函数的参数和返回值可以是任意类型的对象,这就意味着函数本身也可以作为函数的参数或返回值,这就是所谓的高阶函数。
如果我们希望上面的calc
函数不仅仅可以做多个参数求和,还可以做多个参数求乘积甚至更多的二元运算,我们就可以使用高阶函数的方式来改写上面的代码,将加法运算从函数中移除掉,具体的做法如下所示。
1 |
|
注意,上面的函数增加了两个参数,其中init_value
代表运算的初始值,op
代表二元运算函数。经过改造的calc
函数不仅仅可以实现多个参数的累加求和,也可以实现多个参数的累乘运算,代码如下所示。
1 |
|
通过对高阶函数的运用,calc
函数不再和加法运算耦合,所以灵活性和通用性会变强,这是一种解耦合的编程技巧,但是最初学者来说可能会稍微有点难以理解。需要注意的是,将函数作为参数和调用函数是有显著的区别的,调用函数需要在函数名后面跟上圆括号,而把函数作为参数时只需要函数名即可。上面的代码也可以不用定义add
和mul
函数,因为Python标准库中的operator
模块提供了代表加法运算的add
和代表乘法运算的mul
函数,我们直接使用即可,代码如下所示。
1 |
|
Python内置函数中有不少高阶函数,我们前面提到过的filter
和map
函数就是高阶函数,前者可以实现对序列中元素的过滤,后者可以实现对序列中元素的映射,例如我们要去掉一个整数列表中的奇数,并对所有的偶数求平方得到一个新的列表,就可以直接使用这两个函数来做到,具体的做法是如下所示。
1 |
|
当然,要完成上面代码的功能,也可以使用列表生成式,列表生成式的做法更为简单优雅。
1 |
|
三、 常用高阶函数
主要包括map,reduce,filter
三个函数
3.1 map()
我们先看map。map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。
举例说明,比如我们有一个函数f(x)=$x^2$,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]
上,就可以用map()
实现如下:
1 |
|
现在,我们用Python代码实现:
1 |
|
map()
传入的第一个参数是f
,即函数对象本身。由于结果r
是一个Iterator
,Iterator
是惰性序列,因此通过list()
函数让它把整个序列都计算出来并返回一个list。
3.2 reduce()
再看reduce
的用法。reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算,其效果就是:
1 |
|
比方说对一个序列求和,就可以用reduce
实现:
1 |
|
3.3 filter()
Python内建的filter()
函数用于过滤序列。
和map()
类似,filter()
也接收一个函数和一个序列。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
1 |
|
把一个序列中的空字符串删掉,可以这么写:
1 |
|
3.4 高阶函数的结合使用
1 |
|
一个str2int
的函数表示成:
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!