1. Recursion function
1.1 概念
在定义一个函数的时候,在函数内部直接或者间接的调用了本省这个函数,就称为递归函数。
def test():
...
return test()
这个函数直接调用了自身。定义了test这个函数,它的返回值是test(),就是说调用了test()的返回值,但是test()的值是test(),这样来回不断地调用函数,如果没有结束条件,就是一个死循环,不过也是递归。
def a():
...
b()
...
return ...
def b():
...
return a()
这种在函数内部调用了某一个函数,这个被调用的函数内部又调用了原函数,这样叫间接调用,经常出现死循环,而且错误不好查找。
1.2 要求
递归函数一定要间接或者直接的调用自身。
递归函数一定要有边界条件,不能无休止的递归。
不满足这个边界条件是,递归继续,函数执行,
满足这个边界条件时,递归停止,函数结束。
递归函数的层数不宜过多,递归层数太多的话会引起效率问题。python对递归函数的深度(层数)做了限制。不过限制的层数可以手动调整。
1.3 例子
求n的阶乘,是一个明显的可以用递归函数解决的问题:
def accumlate(n, outcome=1):
if n < 2: #这里是边界条件,当实参n小于2时,就返回outcom
return outcome
else:
outcome *= n
return accumlate(n-1, outcome)
#这里是迭代,如果没到边界条件,每次都返回此函数,
但是实参每次都改变。
还有一个典型的可以用递归函数的是fibonacii sequence:
def fibnacii(n, a=1, b=1, seq=None):
if seq is None: #这么写是想练一练这个技巧
seq = []
if n < 3: #这是边界条件
if n == 1: #如果实参给的1,那么就是[1],特殊情况
return [1]
else: #如果给的是2,也是特殊情况,如果直接给2,seq是[]
return [1,1] + seq
else:
a, b = b, a + b #和循环一样的思路,三个数子a, b, a+b来回换
seq.append(b)
return fibnacii(n-1, a, b, seq)
#迭代,每次迭代n都要减1,然后把新算的a, b(其实是a+b)
覆盖原有的a, b,把append好的seq传入。
2. 调用栈
2.1 基本概念
函数在执行的时候,会用到调用栈这个数据结构。
栈,stack,特点是后进先出。栈和多线程有关,每一个线程有一个栈,不同的线程之间互不干扰。
可以把栈想象成一叠便条,以下面这个函数为例:
def main(string): #这些string都是形参,品品
print(string)
def inner(string):
print(string)
return inner(string)
调用这个函数,就会形成一个调用栈:
main('fuck')
首先,把一张main()函数的便条,放在桌子上;
再压上main()要用的数据 ‘fuck’ 的便条;
再压上print()函数的便条;
执行main的print(‘fuck’),执行结束后撕掉。现在只剩一个main()函数的便条。
又调用了inner()函数,就是再在main()函数上压上inner()便条;
再压上inner()要用的数据 ‘fuck’ 的便条;
再压上print()函数的便条;
执行inner的print(‘fuck’),执行结束撕掉。现在只剩一个main()函数的便条。
执行全部结束了,把main()函数的便条也撕掉。
往上压便条,就叫做压栈,撕掉便条,就叫弹出栈,比如一个局部变量在函数内部调用时创建,就是压栈,调用结束后消亡,就是从栈弹出,所以在外部找不到这个局部变量了。
栈内的一条语句和数据执行完,就弹出,然后往下走根据代码压栈-执行-弹出。
2.2 栈的深度
递归函数在创建调用栈时,因为是不断地调用自身,所以调用栈会压入很多层,所以说递归函数的性能很差。
python的调用栈有层数限制,可以更改,但最好不要更改,可以通过sys模块下的getrecursionlimit()来查看:
import sys
print(sys.getrecursionlimit())
这个深度界限用代码计数的话可能不符,因为这个函数执行,可能还需要先压入别的函数和数据。
3. lambda表达式
python中有一种函数,他没有名字,只能写在一行里。也叫做匿名函数。
格式如下:
lambda 参数列表:表达式
(lambda 参数列表:表达式)(实际参数)
lambda x, y: x**2 + y**3 #这是函数的定义
fn = lambda x, y: x**2 + y**3 #,可以用标识符来接,然后调用
fn(2, 3)
31
(lambda x, y: x**2 + y**3)(2, 3) #也可以这么调用
31
这种lambda表达式定义的函数,在高阶函数中运用十分广泛。
lambda表达式的返回值就是表达式的结果。
lambda表达式里不能出现赋值语句。