python 之 生成器函数 和 递归函数

生成器

生成器指的是生成器对象,可以由生成器表达式得到,也可以使用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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值