Python函数递归及三元表达式与匿名函数
一、递归
1.1 函数递归调用介绍
函数不仅可以嵌套定义,还可以嵌套调用,即在调用一个函数的过程中,函数内部又调用另一个函数;而函数的递归调用指的是在调用一个函数的过程中又直接或间接地调用该函数本身。
例如:在调用f1的过程中,又调用f1,这就是直接调用函数f1本身:
def f1():
print('from f1')
f1()
f1()
在调用f1的过程中,又调用f2,而在调用f2的过程中又调用f1,这就是间接调用函数f1本身:
def f1():
print('from f1')
f2()
def f2():
print('from f2')
f1()
f1()
从上图可以看出,两种情况下的递归调用都是一个无限循环的过程,但Python中对函数的递归调用的深度做了限制(默认1000次),因而并不会像大家所想的那样进入无限循环,会抛出异常;
要避免出现这种情况,就必须让递归调用在满足某个特定条件下终止;
提示:
- 可以使用sys模块下的sys.getrecursionlimit()去查看递归深度,默认值为1000,虽然可以使用sys.setrecursionlimit()去设定该值,但仍受限于主机操作系统栈大小的限制;
- Python不是一门函数式编程语言,无法对递归进行尾递归优化;
1.2 回溯与递推
下面会用一个浅显的例子,为了让读者阐释递归的原理和使用:
例1:
某公司四个员工坐在一起,问第四个人薪水,他说比第三个人多1000,问第三个人薪水,他说比第二个人多1000,问第二个人薪水,他说比第一个人多1000,最后第一个人说自己每月5000,请问第四个人的薪水是多少?
思路解析:
要知道第四个人的月薪,就必须知道第三个人的,第三个人的又取决于第二个人的,第二个人的又取决于第一个人的,而且每一个员工都比前一个多一千,数学表达式即:
salary(4) = salary(3) + 1000
salary(3) = salary(2) + 1000
salary(2) = salary(1) + 1000
salary(1) = 5000
总结为:
salary(n) = salary(n - 1) + 1000 (n > 1)
salary(1) = 5000 (n = 1)
很明显这是一个递归的过程,可以将该过程分为两个阶段:回溯和递推。
先进入回溯阶段:要求第n个员工的薪水,需要回溯得到(n - 1)个员工的薪水,以此类推,直到得到第一个员工的薪水,此时,salary(1)已知,因而不必再向前回溯了;
然后进入递推阶段:从第一个员工的薪水可以推算出第二个员工的薪水(6000),从第二个员工的薪水可以推算出第三个员工的薪水(7000),以此类推,一直推算出第四个员工的薪水(8000)为止,递归结束;
需要注意的一点是,递归一定要有一个结束条件,这里n = 1就是结束条件。
代码实现如下:
def salary(n):
if n == 1:
return 5000
return salary(n - 1) + 1000
s = salary(4)
print(s)
执行结果:
8000
程序分析:
在未满足n == 1的条件下,一直进行递归调用,即一直回溯;而在满足n == 1的条件时,终止递归调用,即结束回溯,从而进入递推阶段,依次推导直到得到最终的结果。
递归本质就是在做重复的事情,所以理论上递归可以解决的问题循环也可以解决,只不过在某些情况下,使用递归会更容易实现,比如有一个嵌套多层的列表,要求打印出所有的元素,代码实现如下:
items = [[1, 2], 3, [4, [5, [6, 7]]]]
def foo(items):
for i in items:
if isinstance(i, list): # 满足未遍历完items以及if判断成立的条件时,一直进行递归调用
foo(i)
else:
print(i, end=' ')
foo(items)
打印结果:
1 2 3 4 5 6 7
使用递归,我们只需要分析出要重复执行的代码逻辑,然后提取进入下一次递归调用的条件或者说递归结束的条件即可,代码实现起来简洁清晰。
二、三元表达式、列表生成式,字典生成式
2.1 三元表达式
三元表达式是Python为我们提供的一种简化代码的解决方案,语法如下:
res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
针对下述场景:
def max2(x, y):
if x > y:
return x
else:
return y
res = max2(1, 2)
用三元表达式可以一行解决:
x = 1
y = 2
res = x if x > y else y # 三元表达式
当功能需求仅仅是二选一的情况下,推荐使用三元表达式;尽量不要嵌套使用三元表达式。
2.2 列表生成式
列表生成式是Python为我们提供的一种简化代码的解决方案,用来快速生成列表,语法如下:
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
"""类似于:"""
res = []
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2
...
for itemN in iterableN:
if conditionN:
res.append(expression)
针对下述场景:
egg_list = []
for i in range(10):
egg_list.append('鸡蛋%s' % i)
用列表生成式可以一行解决:
egg_list = ['鸡蛋%s' % i for i in range(10)]
2.3 字典生成式
这里先介绍一个Python内置的函数方法枚举:enumerate():用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,也可以通过start参数控制下标起始位置,一般用在 for 循环当中。
字典生成式一般会搭配enumerate()一起使用,使用方法如下:
name_list = ['jason', 'kevin', 'tony', 'jerry']
res = {i: j for i, j in enumerate(name_list) if j != 'jason'}
print(res)
打印结果为:
{1: 'kevin', 2: 'tony', 3: 'jerry'}
三、匿名函数
3.1 什么是匿名函数?
匿名函数就是没有名字的函数,其语法格式如下:
lambda 形参:返回值
匿名函数与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,除非让其有名字:
func = lambda x, y, z=1:x + y + z
func(1, 2, 3)
"""但让其有名字就失去了匿名函数的意义"""
3.2 有名函数与匿名函数的对比
- 有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能;
- 匿名函数:一次性使用,随时随地定义;
匿名函数一般不会单独使用,都是配合生成式或者其他函数一起使用,如:
max()
min()
sorted()
map()
reduce()
filter()
...