1)先来看看匿名函数。
函数的使用虽然方便,但是往往需要大量的格式定义,即使是很简单的功能,也往往需要很多语句,而且还需要我们给它起个名称。有时在函数定义非常多的场合,这是个很大的负担。所以,匿名函数应运而生。
我们看个例子。比如我们还是获取两个数值中的最大值,这是我们在前面章节定义的函数:
def max(num1, num2):
if num1 > num2:
return num1
else:
return num2
print(max(3, 2))
输出为:3。现在我们换个写法:
max = lambda num1, num2: num1 if num1 > num2 else num2
print(max(3, 2))
输出内容同上。从结果来看,效果完全一样,差别只有定义的方法。
仔细观察,可以看出,匿名函数和原始函数可以写成等价的模式:函数名称=lambda 参数: 函数
它的核心定义在于lambda后面的内容,其实和原来的函数定义一一对应:
其中,冒号之前的变量就是函数的参数,匿名函数不需要return来返回值,表达式本身结果就是返回值。
这里采用了一种类似于变量定义的方式来生成函数,在调用函数时,用法也完全一样。函数体未必一定要有判断,但是一般只能有一条语句,也因此在功能上受限很大。而且代码也进行了很大的简化,比如return在这里就省略了,但是返回数值的效果还存在。
上述代码可以进一步简化:
print((lambda num1, num2: num1 if num1 > num2 else num2)(3, 2))
输出内容同上。前面的函数名称完全替换成了函数的实现,后面直接跟参数,连中间变量也去除,实现了真正的匿名效果。对应关系为:
大家参考这个模式,就可以实现一般函数向匿名函数的转换。另外,由于使用lambda,所以匿名函数也被称为lambda函数。
当然,一直都有人质疑为什么要使用lambda函数,毕竟这种简化的代码会造成代码的可读性下降,从而增加出错的可能和排查错误的难度。大家在使用时多注意观察。
在有些场合,匿名函数可以起到特殊的效果。比如排序函数,通过匿名函数可以设定排序的规则,如:
nums = [-4, 10, 5, -9, 0]
print(sorted(nums, key=lambda a: abs(a)))
输出为:[0, -4, 5, -9, 10]
此时,通过匿名函数可以设定排序的规则。sorted函数的key属性通过匿名函数,可以让sorted函数在判断每个数值时,先通过匿名函数处理下,然后再根据结果值进行排序,而真实的列表数值并不改变。
2)传递函数的参数。
所谓传递函数的参数,可以理解就是把函数作为其他函数的参数,我们可以像使用变量一样使用这个函数,这对于很多函数很方便。
比如我们定义一个求平方的函数:
def squares(x):
return x ** 2
然后再定义一个转换函数,可以从一个列表中依次取出每个元素,应用某个函数处理:
def squares(x):
return x ** 2
def convert(func, seq):
return [func(eachNum) for eachNum in seq]
这里func为函数参数。测试代码为:
def squares(x):
return x ** 2
def convert(func, seq):
return [func(eachNum) for eachNum in seq]
num = [1, 2, 3, 4]
print(convert(float, num))
print(convert(squares, num))
输出为:
[1.0, 2.0, 3.0, 4.0]
[1, 4, 9, 16]
这里convert函数就接收了两个参数,其中第一个就是传递函数的参数。
同样,写成匿名函数的形式也可以进行:
squares = lambda x: x ** 2
def convert(func, seq):
return [func(eachNum) for eachNum in seq]
num = [1, 2, 3, 4]
print(convert(float, num))
print(convert(squares, num))
输出内容同上。
这其实反映了一个有趣的用法,就是在Python中,我们可以像使用变量一样使用函数,甚至可以像变量一样赋值。最简单的应用就是利用这个方法可以给现有函数起个别名,实现同样的功能:
squares = lambda x: x ** 2
squares2 = squares
print(squares2(2))
输出为:4,仍然是2的平方。从squares2函数的调用来看,它和调用squares没有区别。
所以,我们不要给变量和函数一样的名称。比如
num = 1
str = 'Hello'
print('[' + str(num) + ']')
运行界面为:
若定义了新str字符串变量的话,最后一行调用str函数的用法就不再表现为对转换字符类型的str函数的调用。
3)递归函数
递归函数只是一种特殊的函数定义方法,也就是说在函数内部继续调用自己,形成一种嵌套的调用关系。
先看看一个例子,计算一个整数的阶乘,如5的阶乘(5!)就为1*2*3*4*5=120。这是一种常见的循环语句练习:
factorial = 1
for i in range(1, 6):
factorial *= i
print(factorial)
输出为:120。
我们换个思路来思考。比如对于一个数n,要计算n的阶乘,我们可以把它理解为n*(n-1),即n乘以n-1的阶乘。此时n-1的阶乘其实又是一个阶乘的计算。不妨写出代码:
def factorial(n):
return n * factorial(n - 1)
print(factorial(5))
这里把阶乘写成一个函数,在这个函数内部,我们再次调用了函数自己,只是参数不同。这就是递归函数。
不过,运行结果并不正确,甚至出现一个错误,说是超出了最大递归深度,即嵌套的次数。
我们不妨自己运行下代码:
实到了这里,可能你就已经看出,这个运行是无穷无尽的。
因此需要我们增加一个必要的中断条件。当n=1时,其实就无需再进行递归了,1的阶乘就是1。我们可以直接返回这个1的结果。只有当n不等于1时,才需要进行上述的函数递归运算:
def factorial(n):
if n > 1:
return n * factorial(n - 1)
else:
return 1
print(factorial(5))
我们不妨再次自己运行下,看看究竟是怎么得到的结果:
虽然递归提供了一种简单有效的代码逻辑实现方法,不过,从性能上看,递归其实效率不高,而且递归的深度有限,所以在实际数据分析中,我们往往并不建议大家使用。
配套学习资源、慕课视频: