# 递归

递归是什么?
举个例子:

def demo(n:int):
    count = 1
    if n ==100:
        return count
    else:
        return count + demo(n+1)

print(demo(2)) # 输出99

约等于循环99次+1
在函数中调用函数本身就是递归

递归必须要准备一个退出分支,例如本段的if n==100:就是退出分支

如果没有退出分支:

def demo(n:int):
    count = 1
    return count + demo(n+1)
# RecursionError: maximum recursion depth exceeded

python有最大递归深度,一般为1000层,部分解释器由于配置可能会有±10层的区别
假如这个函数在正常情况的递归深度超过了1000层,可以使用下面的代码调整最大深度

import sys
sys.setrecursionlimit(10000) # 层数

然后你就会发现:代码执行不完就会报错进程已结束,退出代码-1073741571 (0xC00000FD)
这是因为栈溢出了,建议减少递归层数或进行尾递归优化

生成器在后面会讲到

# 匿名表达式和函数变量

# 匿名表达式

a = lambda n: n * 10
print(a(10))

这种写法PEP并不建议,它会建议使用def来实现效果,但部分情况下这样会徒增行数
并不会增加可读性

匿名表达式格式:lambda 输入:输出
建议将lambda用于简易函数,大型函数使用def

# 警卫

在lambda后可以使用if...else(警卫)来实现分支结构
例子:

f = lambda a: a*10 if a%2 == 0 else a*5
print(f(10))
print(f(5))

警卫的格式:返回值 if 条件 else 返回值(可以再套一个if)
可用于return等返回值的关键字后

套的层多了可读性很差

# 函数变量

def demo():
    print("hello")

func = demo
func()

变量名 = 方法即为函数变量格式

方法后不带括号,不然就是把返回值作为赋值对象

直接用变量名()就可以调用方法

# 推导式

f = [x for x in range(10)]
print(f) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

推导式格式:表达式 for 变量 in 可迭代数据
警卫同样适用于表达式,如下:

f = [x*2 if x%2 == 0 else x for x in range(10)]
print(f)

# 迭代器和生成器

# 迭代器

list1 = [1,2,3,4,5]
iter1 = iter(list1) # 或list1.__iter__()
print(iter1.__next__()) # 或next(iter1)

对可迭代对象执行iter()操作,就可以得到一个迭代器
对迭代器执行next()方法,就会让指针前进一项并返回
在指针超出范围后,会返回StopIteration错误
一个示意图
迭代器非常省内存,比如一个有一千万个整数的列表,使用列表会占用100MB左右,而迭代器只有50B左右
因为迭代器是惰性的,执行前不会访问
可以使用for循环来快速访问迭代器:

l = [1,2,3,4,5]
i = iter(l)
for j in i:
    print(j)

# 生成器

# 生成器函数

def fibonacci(num: int):
    a, b = 1, 1
    for i in range(num - 1):
        a, b = b, a + b
        yield a

这是一个生成斐波那契数列的函数,尝试直接print它:

print(fibonacci(10))

返回:<generator object fibonacci at 0x0000022C97099B60>
实际上这个函数已经变成了生成器,可以使用for或next来获取返回值
生成器的语法:

def 函数名(参数):
    语句
    yield 返回值
    语句

与return不同,函数遇到yield后不会退出,在返回值后继续执行下面的代码
所以可以有多个yield,返回值格式如下:

值1 值2 值3 值1 值2 值3...