pythonの协程

生成器

生成器概念提出于PEP255,当时的python版本为2.2。

解决的问题

当你想创建一个很小的序列的时候,例如创建从0到100这样的列表,直接使用list似乎没什么问题。

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

但是如果想创建一个从 0 到 999999999 这么大的列表的话,就必须要创建一个完整的,长度是 999999999 的列表,这个行为非常占用内存。

于是就有了生成器。

生成器是迭代器,这是一种迭代器,只能迭代一次。 生成器不会将所有值存储在内存中,它们将在运行时生成值。这样只需在内存中维护可以存储一个值的内存空间就可以了

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

由于mygenerator只能使用一次,所以你不能在mygenerator中执行第二次:它们计算0,然后忘记它并计算1,并逐个结束计算4。这样只需在内存中维护可以存储一个整数的内存空间就可以了。

yield

yield是一个像return一样使用的关键字;

不同之处是该函数将返回一个生成器;

生成器一般使用for .. in这种迭代方式调用;

当函数第一次调用生成器时,执行遇到yield的时候,会暂停执行,并返回yield后跟的变量;

下次调用for .. in迭代时,则会从上次暂停处继续执行,直到函数遇不到yield(一般为循环结束或者满足return条件)

注:return的值不会被for .. in 迭代接收

# when you call the function, the code you have written in the function body does not run.
# The function only returns the generator object.
# Then, your code will be run each time the for uses the generator.
# The first time the for calls the generator object created from your function, it will run the code in your function from the beginning until it hits yield, then it'll return the first value of the loop.
# Then, each other call will run the loop you have written in the function one more time, and return the next value.
# until there is no value to return.

>>> def lazy_range(max_num):
...     print(1)
...     i = 0
...     print(2)
...     while i < max_num:
...             print(3)
...             yield i
...             print(4)
...             i += 1
...             print(5)
...
>>> print(lazy_range(3))
<generator object lazy_range at 0x000000000267BFC0>
>>> for i in lazy_range(3):
...     print('for-',i)
...
1
2
3
for- 0
4
5
3
for- 1
4
5
3
for- 2
4
5
无return
>>> def lazy_range_1(max_num):
...     print(1)
...     i = 0
...     print(2)
...     while i < max_num:
...             print(3)
...             if i == 3:
...                     print(4)
...                     return 666
...                     print(5)
...             yield i
...             print(6)
...             i += 1
...             print(7)
...
>>> print(lazy_range_1(5))
<generator object lazy_range_1 at 0x000000000267BFC0>
>>> for i in lazy_range_1(5):
...     print('for-',i)
...
1
2
3
for- 0
6
7
3
for- 1
6
7
3
for- 2
6
7
3
4
有return

yield from

yield from委托生成器的概念提出于PEP380

yield from,这个东西可以让你从迭代器中返回任何值(这里用的是迭代器,因为生成器也是一种迭代器),也可以让你重构生成器。

"yield from" 的一个行为就是会在内部自动捕获StopIterator异常,并且将异常对象的value属性设置为yield from表达式的值。

yield from的主要功能类似于一个 "通道" 或者一个 "委托器",用于将协程的调用方和嵌套协程最内层的子生成器连接起来,让二者可以愉快的发送和生产值。

委托生成器(delegating generator):包含yield from <iterable>结构的生成器函数。

子生成器(subgenerator):从yield from表达式中iteratable部分获取的生成器。

理解:

yield from后跟的迭代器(生成器也是一种迭代器),一般为子生成器,yield from会在子生成器每生成一个值的时候,将值传给yield from生成器的调用方,并执行调用方的for .. in 迭代中的代码,并且在子生成器迭代完毕后,将最终的return值返回给yield from处

yield from只接受子生成器return的值,因为子生成器生成的值,即yield后跟的变量,返回给了yield from的调用处

>>> def yf_1():
...     print('yf1-',1)
...     yield from [1,2,3,4,5]
...     print('yf1-',2)
...
>>> for i in yf_1():
...     print('main-',i)
...
yf1- 1
main- 1
main- 2
main- 3
main- 4
main- 5
yf1- 2
>>> def yf_2():
...     print('yf2-',1)
...     yield from lazy_range(3)
...     print('yf2-',2)
...
>>> for i in yf_2():
...     print('main-',i)
...
yf2- 1
1
2
3
main- 0
4
5
3
main- 1
4
5
3
main- 2
4
5
yf2- 2
>>> def yf_3():
...     print('yf3-',1)
...     a = yield from lazy_range_1(5)
...     print('yf3-',2)
...     print('a>',a)
...     print('yf3-',3)
...
>>> for i in yf_3():
...     print('main-',i)
...
yf3- 1
1
2
3
main- 0
6
7
3
main- 1
6
7
3
main- 2
6
7
3
4
yf3- 2
a> 666
yf3- 3

async/await

为了简化并更好地标识异步IO。从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。

python3.5注意:

       await 只能用于 async def 的函数中;

       yield from 不能用于 async def 的函数中;

       await接受的对象必须是一个awaitable对象(一个实现了__await()__方法的对象,而且这个方法必须返回一个不是协程的迭代器)

# python34
@asyncio.coroutine
def py34_function():
    yield from work()

# python35
async def py35_function():
    await work()

在 Python3.6 中,这种特性继续被发扬光大,现在可以在同一个函数体内使用yield和await,而且除此之外,也可以在列表推导等地方使用async for或await语法。

result = [i async for i in aiter() if i % 2]
result = [await func() for fun in funcs if await condition()]

async def test(x, y):
    for i in range(y):
        yield i
        await asyncio.sleep(x)

生成器的另一种调用方式

next()和send()

注:此种方式需要预激生成器,yield from会自动预激生成器,并且会自动catch到StopIteration异常,并将yield返回值返回到yield from的调用处

>>> a=yf_1()
>>> a
<generator object yf_1 at 0x000000000267BFC0>
>>> a.send(None) # 预计prime
yf1- 1
1
>>> next(a)
2
>>> next(a)
3
>>> next(a)
4
>>> next(a)
5
# 最后会抛出一个StopIteration异常,因为代码执行完毕了,这就是生成器的标准行为,不用在意。
>>> next(a)
yf1- 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

多协程使用举例

向子生成器传值

https://lightless.me/archives/python-coroutine-from-boom-to-dead-to-rebirth.html

模拟IO操作

>>> @asyncio.coroutine
... def coroutine_t(sleep_time):
...     print('ct-',1)
...     yield from asyncio.sleep(sleep_time)
...     print('ct-',2)
...
>>> def main():
...     print('main',1)
...     loop = asyncio.get_event_loop()
...     print('main',2)
...     tasks = [
...             asyncio.ensure_future(coroutine_t(1)),
...             asyncio.ensure_future(coroutine_t(2)),
...             asyncio.ensure_future(coroutine_t(3)),
...             asyncio.ensure_future(coroutine_t(4)),
...             asyncio.ensure_future(coroutine_t(5)),
...     ]
...     print('main',3)
...     loop.run_until_complete(asyncio.wait(tasks))
...     print('main',4)
...     loop.close()
...     print('main',5)
...
>>> main()
main 1
main 2
main 3
ct- 1
ct- 1
ct- 1
ct- 1
ct- 1
ct- 2 # wait 1s
ct- 2 # wait 2s
ct- 2 # wait 3s
ct- 2 # wait 4s
ct- 2 # wait 5s
main 4
main 5

爬虫例子(同步16s -> 多线程10s -> 多协程2s)

https://lightless.me/archives/python-coroutine-from-boom-to-dead-to-rebirth.html

红绿灯demo

https://github.com/HanChengITer/PyStudy/blob/master/parallel_tasks/transportation_coroutines.py

参考资料

Python 协程从零开始到放弃

https://lightless.me/archives/python-coroutine-from-start-to-boom.html

Python 协程之从放弃到死亡再到重生

https://lightless.me/archives/python-coroutine-from-boom-to-dead-to-rebirth.html

python中重要的模块--asyncio

https://www.cnblogs.com/zhaof/p/8490045.html

posted @ 2018-06-25 21:20  yc紫日  阅读(207)  评论(0编辑  收藏  举报