Python——生成器详解

写在前面

生成器的基础是迭代器,所以在学习本博客之前,一定要先学习迭代器

1. 什么是生成器

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前的状态生成下一个数据。为了达到记录当前的状态,并配合next()函数进行迭代使用,我们可以采用更加简便的语法,即生成器。生成器是一种特殊的迭代器

2.创建生成器的两种方法

2.1 类似列表解析

我们都清楚列表解析,举个例子:

data = [x*2 for x in range(5)]
for i in data:
	print(i,end=' ')

# result
0,2,4,6,8

当我们将列表解析中的 “[]” 换成 " () "时,就创建了一个生成器,即:

data = (x*2 for x in range(5))
print(data)

# result
<generator object <genexpr> at 0x0000027968E71D48>

# 考虑生成器时特殊的迭代器,故我们可以调用next()方法获取下一个值

print(next(data))
# result
0

2.2 函数中使用yield,则函数为生成器

函数中没有yield关键字的还是原本的函数,函数中一旦出现yield关键字,函数不再是函数,而是生成器

def test():
	yield 10
m = test()
print(m)

# result 可以看到函数返回的是一个生成器对象
<generator object test at 0x0000027968E71DC8>


# 调用next()获取下一个值
next(m)  # 10

2.3 用斐波那契例子体验yield的作用

yield的作用

  • 1)充当return作用
  • 2)保存程序的运行状态,并且暂停程序执行
  • 3)当调用next时,可以继续唤醒程序从yield的位置继续向下执行

用生成器生成斐波那契数列

代码思路:

  • 1)定义斐波那契生成器(定义函数的方式)
  • 2)用next()方法生成数字
def fib(n):
	a = 1
	b = 1
	current_index = 0
	while current_index < n:
		data = a
		a,b = b,a+b
		current_index += 1
		yield data 

fib = fib(5) # 创建了一个生成器
print(next(fib)) # 1
print(next(fib)) # 1
print(next(fib)) # 2
print(next(fib)) # 3
print(next(fib)) # 5
print(next(fib)) # StopIteration Error

这个例子很简单,我们重点理解yield在该函数中执行的流程:

  • fib = fib(5)该语句将参数5传入到生成器内,开始进入生成器内部执行代码
  • 当执行第一个print(next(fib))语句时,此时data = 1,则yield将data=1返回(这里体现了yield充当return的功能),此时程序停在了 yield data 这条语句上
  • 当执行第二个print(next(fib))语句时,此时程序在上一次的暂停处被唤醒,也即在yield data处被再次唤醒,程序接着执行,接着进行while循环,直到再次遇见yield data。这里体现了yield的保存程序的运行状态,并且暂停程序执行的作用,因为每次生成的数值都不一样,说明程序保存了上一次的状态
  • 以此类推,每次程序到yield data语句都会被终止暂停,直到再次调用next()方法,程序从上一次终止处继续执行,再次遇到yield,又暂停,反反复复,直到循环条件不满足或者自动抛出StopIteration Error结束

为了更加清晰地认识到yield作用,我们为上述代码增加几行print语句:

def fib(n):
	a = 1
	b = 1
	current_index = 0
	print('111111111111111111111') # add this 
	while current_index < n:
		data = a
		a,b = b,a+b
		current_index += 1
		print('2222222222222222222') # add this 
		yield data 
		print('3333333333333333333') # add this 

fib = fib(5) # 创建了一个生成器

next(fib)  # 程序停在yield data 处

# result
'11111111111111111111111'
'22222222222222222222222'

# 当我们再次调用next方法时,程序在yield处被唤醒,继续执行
next(fib)

# result
'33333333333333333333333'
'22222222222222222222222'

3. 生成器中使用return问题

考虑上面斐波那契生成器,我们因为规定了while循环所以可以使生成器停止运行,假如我们没有while循环来控制生成器,那么可以一直用next()方法去生成,从而进行无终止地迭代。那么我们怎么让生成器停止并推出呢?

使用return终止生成器:一旦遇到return,则终止生成器,异常信息为return的内容

def fib(n):
	a = 1
	b = 1
	current_index = 0
	while current_index < n:
		data = a
		a,b = b,a+b
		current_index += 1
		yield data 
		return '终止迭代器'

fib = fib(5) # 创建了一个生成器

print(next(fib)) # 输出1,此时程序执行到了yield data处
print(next(fib)) # 从yield data处继续执行,遇到return抛出StopIteration: 终止迭代器

一般我们在写生成器时,yield 数据左边都会有一个接收值x,这是为了使用生成器对象的send函数,接下来介绍send函数:

def fib(n):
	a = 1
	b = 1
	current_index = 0
	while current_index < n:
		data = a
		a,b = b,a+b
		current_index += 1
		x = yield data  # 用x接收值
		print(x)
		return '终止生成器'

fib = fib(5) # 创建了一个生成器
print(next(fib)) # 输出1,此时程序执行到了yield data处,注意执行的是等号右边,左边没有执行
print(next(fib)) # 执行yield data 左边,但是此时x并没有值,所以为None.
# result
1
None
StopIteration: 终止迭代器

那么x怎么才能有值呢,有值以后又能干什么呢?

3.1 使用生成器对象的函数send()

  • send也可以将程序唤醒,并且send函数接收一个参数用来传递给x,这是next函数做不到的地方,next函数不能给x传递值。
def fib(n):
	a = 1
	b = 1
	current_index = 0
	while current_index < n:
		data = a
		a,b = b,a+b
		current_index += 1
		x = yield data  # 用x接收值
		print(x)
		return '终止生成器'

fib = fib(5) # 创建了一个生成器

print(next(fib))  # 程序停在yield data 处
print(fib.send(2)) # 程序从yield data 处被唤醒继续执行,并且将2传递给x

# result
1
2
StopIteration: 终止迭代器

所以我们可以用send函数来控制生成器退出:

def fib(n):
	a = 1
	b = 1
	current_index = 0
	while current_index < n:
		data = a
		a,b = b,a+b
		current_index += 1
		x = yield data  # 用x接收值
		print(x)
		if x == 2:
			return '终止生成器'

fib = fib(5) # 创建了一个生成器
print(next(fib))  # 程序停在yield data 处
print(fib.send(1)) # x值为1,不退出生成器
print(fib.send(2)) # x值为2,退出生成器

# result
1 # 这是生成的数据
1 # print(x)产生的1
1 # 这是生成的数据
2 # print(x)产生的2
StopIteration: 终止迭代器

小结:

  • return作用:结束生成器的运行
  • send作用:生成器对象.send(传递给生成器的值)

猜你喜欢

转载自blog.csdn.net/weixin_44441131/article/details/107685029