17. 生成器

村里唯一的运维 / 2023-05-11 / 原文

1. 概述

元组推导式是生成器(generator)
生成器本质是迭代器,允许自定义逻辑的迭代器

迭代器和生成器区别:
	迭代器本身是系统内置的.重写不了.而生成器是用户自定义的,可以重写迭代逻辑

生成器可以用两种方式创建:
    (1)生成器表达式  (里面是推导式,外面用圆括号)
    (2)生成器函数    (用def定义,里面含有yield)

2. 生成器的基本用法

生成器表达式

gen = ( i for i in range(5))
print(gen,type(gen))

image.png

生成器中获取数据的方式

for循环

gen = ( i for i in range(5))
for i in gen:
	print(i)

next调用

gen = ( i for i in range(5))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)

for循环和next配合调用生成器中的数据

gen = ( i for i in range(5))
for i in range(5):
	res = next(gen)
	print(res)

3. 生成器函数

yield 类似于 return
共同点在于:执行到这句话都会把值返回出去
不同点在于:yield每次返回时,会记住上次离开时执行的位置 , 下次在调用生成器 , 会从上次执行的位置往下走
		   而return直接终止函数,每次重头调用.

生成器函数基本用法

def mygen():
	print("one")
	yield 1	
	print("two")
	yield 2	
	print("three")
	yield 3
    
初始化生成器函数 返回生成器对象, 简称生成器
gen = mygen()
print(isinstance(gen,Iterator))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
# res = next(gen) error 越界错误
# print(res)

上述代码解析

代码解析:
通过next调用生成器对象,找到mygen函数,代码依次从上到下执行:
先走12行 print("one")  yield 1 记录当前执行代码的位置状态 , 添加阻塞 ,把1直接返回,等待下一次调用

在通过next进行调用时,
从上一次记录的那个位置状态往下走,

print("two") yield 2 记录当前执行代码的位置状态 , 添加阻塞 ,把2直接返回,等待下一次调用

在通过next进行调用时,
从上一次记录的那个位置状态往下走,

print("three") yield 3 记录当前执行代码的位置状态 , 添加阻塞 ,把3直接返回,等待下一次调用

在通过next进行调用时,
从上一次记录的那个位置状态往下走,发现已经没有yield,直接越界报错.

send

不但可以获取值,还能发送值,发送给上一个yield

next和send区别:
	next 只能取值
	send 不但能取值,还能发送值
send注意点:
	第一个 send 不能给 yield 传值 默认只能写None
	最后一个yield 接收不到send的发送值

示例说明

def mygen():
	print("program start")
	res = yield 1
	print(res)
	res = yield 2
	print(res)
	res = yield 3
	print(res)
	print("program end")
# 初始化生成器函数 => 变成生成器对象 简称 生成器
print("<===================>")
gen = mygen()
res2 = gen.send(None)
print(res2)
res2 = gen.send(1111)
print(res2)
res2 = gen.send(2222)
print(res2)

image.png
代码解析

代码解析:
res = gen.send(None) 第一次通过send 调用时,因为send 默认发送给上一个yield,所以第一次发送不了,只能默认发送None,属于硬性语法
第一次代码调用时,只走到了3行 ,记录代码执行的位置状态,添加阻塞,返回值1

在调用时,通过send(1111)给yield 1 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走4行代码,打印 1111
继续执行5行代码, 把yield 2返回,记录代码执行的位置状态,添加阻塞,返回值2

在调用时,通过send(2222)给yield 2 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走6行代码,打印 2222
继续执行7行代码, 把yield 3返回,记录代码执行的位置状态,添加阻塞,返回值3

在调用时,通过send(3333)给yield 3 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走85行代码,打印 3333
问题是再也没有yield 响应的返回了,直接报错.

yield from : 将一个可迭代对象变成一个迭代器返回

系统默认会把from后面的可迭代性数据,每个元素扔到迭代器中,通过next一个一个调用

def mygen():
	yield from ["张三","李四","王五","大刀"]
gen = mygen()
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)

image.png