生成器
生成器指的是生成器对象,可以由生成器表达式得到,也可以使用yield关键字得到一个生成器函数,调用这个函数得到一个生成器对象
#生成器本质是迭代器,允许自定义逻辑的迭代器
#迭代器和生成器区别:
迭代器本身是系统内置的.重写不了.
而生成器是用户自定义的,可以重写迭代逻辑
#生成器可以用两种方式创建:
(1)生成器表达式 (里面是推导式,外面用圆括号)
(2)生成器函数 (用def定义,里面含有yield)
生成器表达式
gen = (i*2 for i in range(1,11))
next 调用
res= mext(gen)
print(res)
for 调用
for i in gen:
print(i)
for +next 调用
gen = (i*2 for i in range(1,11))
for i in range(3):
res= next(gen)
print(res)
list 强转
print(list(gen)))
生成器函数
函数体中包含yield语句的函数,就是生成器函数,调用后返回生成器对象
yield 类似于 return
共同点在于:执行到这句话都会把值返回出去
不同点在于:yield每次返回时,会记住上次离开时执行的位置 , 下次在调用生成器 , 会从上次执行的位置往下走
而return直接终止函数,每次重头调用.
yield 6 和 yield(6) 2种写法都可以 yield 6 更像 return 6 的写法 推荐使用
基本语法:
def mygen():
print(111)
yield 1
print(222)
yield 2
print(333)
yield 3
# 初始化生成器函数,返回生成器对象,简称生成器
gen= mygen()
next 调用
res=next(gen)
print(res)
res=next(gen)
print(res)
res=next(gen)
print(res)
res=next(gen)
print(res)
代码解析:
初始化生成器函数 -> 生成器(通过next调用)
第一次调用生成器
res = next(gen) => print(111) yield 1 保存当前代码状态yield 1这行,并将1这个值返回 print(1) ,等待下一次调用
第二次调用生成器
res = next(gen) => 从上一次保存的状态行继续向下执行
print(222) yield 2 保存当前代码状态yield 2这行,并将2这个值返回 print(2) ,等待下一次调用
第三次调用生成器
res = next(gen) => 从上一次保存的状态17行继续向下执行
print(333) yield 3 保存当前代码状态yield 3这行,并将3这个值返回 print(3) ,等待下一次调用
第四次调用生成器
因为没有更多yield返回数据了,所以直接报错.
send用法:
next和send区别:
next 只能取值
send 不但能取值,还能发送值
send注意点:
第一个 send 不能给 yield 传值 默认只能写None
最后一个yield 接受不到send的发送值
send 是给上一个yield发送值
def mygen():
print("process start")
res = yield 100
print(res,"内部打印1")
res = yield 200
print(res,"内部打印2")
res = yield 300
print(res,"内部打印3")
print("process end")
# 初始化生成器函数 -> 生成器
gen = mygen()
# 在使用send时,第一次调用必须传递的参数是None(硬性语法),因为第一次还没有遇到上一个yield
第一次调用
res = gen.send(None) #<=> next(gen)
print(res,type(res))
第二次调用
res = gen.send(101) #<=> next(gen)
print(res)
第三次调用
res = gen.send(201) #<=> next(gen)
print(res)
第四次调用, 因为没有更多的yield返回数据了,所以StopIteration'''
res = gen.send(301) #<=> next(gen)
print(res)
# 代码解析:
初始化生成器函数,返回生成器对象
第一次调用时,
print("process start")
res = yield 100 记录当前代码状态行,返回100,等待下一次调用
res = 100 print(100)
第二次调用时,
把101 发送给上一个yield保存的状态行 res = 101 从yield 100这行继续往下走
print(101,"内部打印1")
res = yield 200 记录当前代码状态行,返回200,等待下一次调用
res = 200 print(200)
第三次调用时,
把201 发送给上一个yield保存的状态yield 200这行 res = 201 从这行继续往下走
print(201,"内部打印2")
res = yield 300 记录当前代码状态行,返回300,等待下一次调用
res = 300 print(300)
yield form :将一个可迭代对象变成一个迭代器返回
def mygen():
yield from ["苹果","火龙果","西瓜","桃子"]
gen = mygen()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
用生成器描述斐波那契数列 1 1 2 3 5 8 13 21 34 ...
def mygen(maxlen):
a,b = 0,1
i = 0
while i < maxlen:
yield b
a,b = b,a+b
i+=1
# 初始化生成器函数 -> 生成器
gen = mygen(10)
for i in range(3):
print(next(gen))
递归函数
概念: 自己调用自己的函数就是递归函数
一去一回就是递归
def digui(n):
print(n,"<====1===>")
if n > 0:
digui(n-1)
print(n,"<====2===>")
digui(5)
# 代码解析:
去的过程:
n = 5 print(5,"<====1===>") 5>0 条件成立-> digui(5-1) => digui(4) 代码阻塞在第这行
n = 4 print(4,"<====1===>") 4>0 条件成立-> digui(4-1) => digui(3) 代码阻塞在第这行
n = 3 print(3,"<====1===>") 3>0 条件成立-> digui(3-1) => digui(2) 代码阻塞在第这行
n = 2 print(2,"<====1===>") 2>0 条件成立-> digui(2-1) => digui(1) 代码阻塞在第这行
n = 1 print(1,"<====1===>") 1>0 条件成立-> digui(1-1) => digui(0) 代码阻塞在第这行
n = 0 print(0,"<====1===>") 0>0 条件不成立 print(0,"<====2===>")
当前这层空间代码已经执行结束
此刻触发回的过程
n = 1 从上一次的代码阻塞位置,继续向下执行 print(1,"<====2===>")
n = 2 从上一次的代码阻塞位置,继续向下执行 print(2,"<====2===>")
n = 3 从上一次的代码阻塞位置,继续向下执行 print(3,"<====2===>")
n = 4 从上一次的代码阻塞位置,继续向下执行 print(4,"<====2===>")
n = 5 从上一次的代码阻塞位置,继续向下执行 print(5,"<====2===>")
到此,递归函数彻底执行结束.
5 4 3 2 1 0 0
每次调用函数时,在内存中都会单独开辟一个空间,配合函数运行,这个空间叫做栈帧空间
(1).递归是一去一回的过程,
调用函数时,会开辟栈帧空间,函数执行结束之后,会释放栈帧空间
递归实际上就是不停的开辟和释放栈帧空间的过程
每次开辟栈帧空间,都是独立的一份,其中的资源不共享
(2).触发回的过程
1.当最后一层栈帧空间全部执行结束的时候,会触底反弹,回到上一层空间的调用处
2.遇到return,会触底反弹,回到上一层空间的调用处,
(3).写递归时,必须给与递归跳出的条件,否则会发生内存溢出,蓝屏死机的情况.
如果递归层数过多,不推荐使用递归
递归练习
1.用递归计算n的阶乘
5!=5*4*3*2*1
常规写法
def func(n):
total = 1
for i in range(n,0,-1):
total *= i
return total
res = func(5)
print(res)
# 递归写法
def jiecheng(n):
if n <= 1:
return 1
return n*jiecheng(n-1)
res = jiecheng(5)
print(res)
return 后面的表达式,一定是先计算完在返回
# 代码解析:
# 去的过程:
n = 5 return 5*jiecheng(5-1) => 5 * jiecheng(4)
n = 4 return 4*jiecheng(4-1) => 4 * jiecheng(3)
n = 3 return 3*jiecheng(3-1) => 3 * jiecheng(2)
n = 2 return 2*jiecheng(2-1) => 2 * jiecheng(1)
n = 1 return 1
# 回的过程:
n = 2 return 2*jiecheng(2-1) => 2 * jiecheng(1) => 2 * 1
n = 3 return 3*jiecheng(3-1) => 3 * jiecheng(2) => 3 * 2 * 1
n = 4 return 4*jiecheng(4-1) => 4 * jiecheng(3) => 4 * 3 * 2 * 1
n = 5 return 5*jiecheng(5-1) => 5 * jiecheng(4) => 5 * 4 * 3 * 2 * 1
return 5 * 4 * 3 * 2 * 1 => return 120
额外解析:
jiecheng(1) => 1
jiecheng(2) => 2*jiecheng(1) => 2*1
jiecheng(3) => 3*jiecheng(2) => 3*2*1
jiecheng(4) => 4*jiecheng(3) => 4*3*2*1
jiecheng(5) => 5*jiecheng(4) => 5* 4*3*2*1
尾递归
自己调用自己,并且非表达式
计算的结果要在参数当中完成.
尾递归无论调用多少次函数,都只占用一份空间,但是目前cpython不支持.
尾递归用法: 计算n的阶乘
def jiecheng(n,endval):
if n <= 1:
return endval
return jiecheng(n-1,endval*n)
res = jiecheng(5,1)
print(res)
# 代码解析:
去的过程
n=5 , endval=1 return jiecheng(5-1,endval*5) => jiecheng(4,1*5)
n=4 , endval=1*5 return jiecheng(4-1,endval*4) => jiecheng(3,1*5*4)
n=3 , endval=1*5*4 return jiecheng(3-1,endval*3) => jiecheng(2,1*5*4*3)
n=2 , endval=1*5*4*3 return jiecheng(2-1,endval*2) => jiecheng(1,1*5*4*3*2)
n=1 , endval=1*5*4*3*2 return 120
回的过程:
n=2 return 120
n=3 return 120
n=4 return 120
n=5 return 120
因为最后一层空间的返回值就是第一层空间的返回值,所有在使用尾递归的时候
不需要考虑回的逻辑过程,就能解决问题.推荐使用.
递归计算斐波那契数列
# 上一个 n-1 上上个 n-2
def feb(n):
# 递归跳出的条件
if n <= 2: # n == 1 or n == 2 => 1
return 1
return feb(n-1) + feb(n-2)
res = feb(5)
print(res)