4.4 特殊的函数定义和使用

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))

我们不妨再次自己运行下,看看究竟是怎么得到的结果:

虽然递归提供了一种简单有效的代码逻辑实现方法,不过,从性能上看,递归其实效率不高,而且递归的深度有限,所以在实际数据分析中,我们往往并不建议大家使用。

配套学习资源、慕课视频:

Python大数据分析-李树青 (njcie.com)icon-default.png?t=M1H3http://www.njcie.com/python/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leeshuqing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值