python yield yield from

发布时间 2023-05-20 20:15:16作者: 贝壳里的星海

python yield

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

参考资料

https://www.cnblogs.com/cnkai/p/7514828.html

http://events.jianshu.io/p/dda2003e1aa2