13.python-yield和yield from
python-yield和yield from
1:可迭代、迭代器、生成器
2:如何运行/激活生成器
3:生成器的执行状态
4:从生成器过渡到协程:yield
可迭代、迭代器、生成器
from collections.abc import Iterable, Iterator, Generator
isinstance(obj, Iterable) # 可迭代对象
isinstance(obj, Iterator) # 迭代器
isinstance(obj, Generator) # 生成器
Iterable:可迭代对象,一般在python中想字符串,list, dict, tuple, set, deque等都是可迭代对象,一个对象只要实现了__iter__
方法的,均可称为可迭代对象。
Iterator:迭代器,一般对象只要实现了__next__
与 __iter__
方法的均可称为迭代器。
Generator:生成器,是在迭代器的基础上(可以用for
循环,可以使用next()
),再实现了yield
。
yield
相当于我们函数里的return
。在每次next()
,或者for
遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。
可迭代象和迭代器,是将所有的值都生成存放在内存中,而生成器则是需要元素才临时生成,节省时间,节省空间。
yield关键字
yield的用法很像return,都是提供一个返回值,但是yield和return的最大区别在于,return一旦返回,则代码段执行结束,但是yield在返回值以后,会交出CUP的使用权,代码段并没有直接结束,而是在此处中断,当调用send()或者next()方法之后,yield可以从之前中断的地方继续执行。
在一个函数中,使用yield关键字,则当前的函数会变成生成器。
def fib(n):
index = 0
a = 0
b = 1
while index < n:
yield b
a,b = b, a+b
index += 1
fib = fib(100)
print(fib)
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
<generator object fib at 0x0000028AB572A270>
1
1
2
3
使用next 在生成器为空的时候会报错StopIteration,使用for 循环可以避免为空的错误
def fib(n):
index = 0
a = 0
b = 1
while index < n:
yield b
a,b = b, a+b
index += 1
fib = fib(3)
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
1
1
2
StopIteration
def fib(n):
index = 0
a = 0
b = 1
while index < n:
yield b
a,b = b, a+b
index += 1
fib = fib(3)
for i in fib:
print(i)
1
1
2
使用send()方法允许我们向生成器中传值。
import time
def fib(n):
index = 0
a = 0
b = 1
while index < n:
sleep = yield b
print(f'等待{sleep}秒,设置n={n}')
time.sleep(sleep)
a,b = b, a+b
index += 1
fib = fib(20)
print(fib.send(None)) # 效果等同于print(next(fib))
print(fib.send(1))
print(fib.send(2))
print(fib.send(3))
print(fib.send(4))
调用fib.send(None)方法,此处作用与next(fib)相同,程序返回当前b的值1, 程序中断。
调用fib.send(2)方法,程序被唤醒,将2传递给yield之前的变量sleep,程序继续运行,直到遇到yield将新的b返回,程序再次中断
如此继续下去,直到程序结束。
生成器的执行状态
GEN_CREATED
等待开始执行
GEN_RUNNING
解释器正在执行(只有在多线程应用中才能看到这个状态)
GEN_SUSPENDED
在yield表达式处暂停
GEN_CLOSED
执行结束
def simple_coro(a):
print('-> Started: a=',a)
b = yield a # 相当于是先返回a,暂停,直到next方法调用后,b赋值为send发送过来的数据,再执行到下一个yield暂停
print('-> Received: b =',b)
c = yield a+b # 先返回a+b,暂停,直到next方法调用后,c赋值为send发送过来的数据,再执行到下一个yield暂停
print('-> Received: c=',c)
my_coro2=simple_coro(14) # 实例化一个协程对象
from inspect import getgeneratorstate # 得到状态的函数
print(getgeneratorstate(my_coro2))
next(my_coro2)
print(getgeneratorstate(my_coro2))
my_coro2.send(28)
my_coro2.send(99)
print(getgeneratorstate(my_coro2))
GEN_CREATED
-> Started: a= 14
GEN_SUSPENDED
-> Received: b = 28
-> Received: c= 99
Traceback (most recent call last):
File "d:/TzxNote/Note/lcodeNoteCards/testpy.py", line 15, in <module>
my_coro2.send(99)
StopIteration
yield from
yield from 所在的函数被称为委托生成器,它主要为调用方和子生成器提供一个双向通道
yield from 后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。
代替内层for循环
def generator():
for i in range(10):
yield i
def generator2():
yield from range(10)
链接子生成器
def generator1():
item = range(3)
for i in item:
yield i
def generator2():
yield 'a'
yield 'b'
yield from generator1()
# yield from iterable本质上等于 for item in iterable: yield item的缩写版
yield from [11, 22, 33, 44]
yield from range(5,10)
for i in generator2():
print(i)
a
b
0
1
2
11
22
33
44
5
6
7
8
9