协程间的Iterable流
sav.channels的Python项目详细描述
通道:协同程序之间的python迭代器流
这个包提供了一个简单易用的Channel
类来发送和
在协同过程之间接收对象。
0.4版,版权所有)Sander Voerman,2019年。
安装
安装sav.channels 来自python包索引的包。见 installing packages 以便进一步指导。
概述
通道是两个协程之间的直接连接,通过它它们可以 发送和接收对象。
单纯形示例
每个通道都由客户端和服务器端组成。在对象的情况下
只向一个方向发送,client()
方法返回接收
结束,可以使用async for
进行迭代。server
方法返回
一个上下文管理器,可以在
正在生成协同程序:
importasynciofromtypingimportAsyncIterator,AsyncGeneratorfromsav.channelsimportChannelfromfooimportFooasyncdefproduce(channel:Channel[Foo,None])->None:asyncwithchannel.server()asserver:awaitserver.asend(Foo("One"))awaitproduce_two(server)awaitserver.asend(Foo("Three"))asyncdefproduce_two(server:AsyncGenerator[None,Foo])->None:awaitserver.asend(Foo("Two"))asyncdefconsume(client:AsyncIterator[Foo])->None:asyncforfooinclient:print(foo)asyncdefmain()->None:channel=Channel()awaitasyncio.gather(consume(channel.client()),produce(channel))asyncio.run(main())
双工示例
由channel.client()
和async with channel.server()
返回的对象
都是asynchronous generators
支持双向交流。下面的例子
演示数据如何在两个方向上流过通道:
importasyncioimportitertoolsfromsav.channelsimportChannelasyncdefletters(channel:Channel[str,int])->None:asyncwithchannel.server()ass:# wait for the clientprint(awaits.asend("A"))# send and receiveprint(awaits.asend("B"))# send and receiveasyncdefnumbers(channel:Channel[str,int])->None:asend=channel.client().asendtry:print(awaitasend(None))# receive onlyforiinitertools.count():print(awaitasend(i))# send and receiveexceptStopAsyncIteration:passasyncdefmain()->None:channel=Channel()awaitasyncio.gather(letters(channel),numbers(channel))asyncio.run(main())
这将产生结果:
A
0
B
1
因此,通过通道发送的第一个项目是
服务器。async with
块通过等待asend(None)
来启动服务器,
在客户端启动并等待第一个项目
接收。当执行从async with
块流出时,服务器是
通过等待aclose()
关闭,这会导致等待的客户机引发
StopAsyncIteration
。
频道的目的
尽管向两个方向发送值的可能性在 在某些情况下,不是什么让频道有趣,也不是为什么我们需要 他们。渠道的目的在于它逆转 可以说,客户机和服务器生成器发送和 屈服值。
使用通道推入管道
如果一个值被发送到服务器生成器中,它将由
客户端生成器。这意味着您可以将客户端生成器传递给自己的
异步生成器函数,并使该函数从
通道,对其进行处理,并将结果传递给另一个生成器
你的处理管道。然而,每次管道请求
另一个来自频道的项目,正在等待
来自server.asend
的结果恢复执行。这意味着从
对于生产者,管道看起来像一个reverse生成器。渠道给予
你不必写下逆向生成器管道的力量
反向发电机功能。
将生成器重构为生成器,反之亦然
通道的服务器上下文用于镜像 异步发电机功能:
asyncwithchan.server()ass:a=awaits.asend('One')b=awaits.asend('Two')c=awaits.asend('Three')asyncdefagen():a=yield'One'b=yield'Two'c=yield'Three'
通道和发生器功能之间的相似性使得
重构。例如,当只有一个协程发送值时
进入一个通道,除此之外它什么也不做(如
上面的例子),它应该
转换为异步生成器函数。另一方面,
异步生成器函数有某些限制
这会让他们变得笨拙。如果需要转动越来越多的功能
进入生成器函数,因为它们需要yield
到其他生成器,
或者如果代码中充满了while True: f((yield))
循环而不是
async for x: f(x)
循环,将生成器函数重构为通道
可能是可取的。
从一个程序推入多个通道
通道允许更大的灵活性,因为您可以发送值 从单个函数或循环进入不同的通道,而 异步发电机功能与同步发电机功能相同 它只能屈服于同一生成器对象的生成器。
有效地委派给另一个生产商
异步生成器函数不支持yield from g
顺序
委派给其他发电机。相反,你必须写
async for x in g: yield x
(在单纯形情况下),这意味着事件
循环每次都必须跳进和跳出委托生成器
在它跳入生成生成器之前,因为
代码要求在每次迭代时更新x
的值。
相反,使用async with
时通道返回的对象可能
将产品委托给另一个协程,如
本文档顶部的第一个示例。
结合异步和同步迭代
StreamChannel
类提供了额外的读写方法
允许发送多个项,甚至同步的非大小化迭代器,
通过通道,而不将控制权传递回
每一件物品。