从流中产生的正确方法是什么?

Zac*_*tes 23 python generator python-3.x python-asyncio yield-from

我有一个Connection用于包含读取和写入asyncio连接流的对象:

class Connection(object):

    def __init__(self, stream_in, stream_out):
        object.__init__(self)

        self.__in = stream_in
        self.__out = stream_out

    def read(self, n_bytes : int = -1):
        return self.__in.read(n_bytes)

    def write(self, bytes_ : bytes):
        self.__out.write(bytes_)
        yield from self.__out.drain()
Run Code Online (Sandbox Code Playgroud)

在服务器端,每次客户端连接时connected创建一个Connection对象,然后读取4个字节.

@asyncio.coroutine
def new_conection(stream_in, stream_out):
    conn = Connection(stream_in, stream_out)
    data = yield from conn.read(4)
    print(data)
Run Code Online (Sandbox Code Playgroud)

在客户端,写出4个字节.

@asyncio.coroutine
def client(loop):
    ...
    conn = Connection(stream_in, stream_out)
    yield from conn.write(b'test')
Run Code Online (Sandbox Code Playgroud)

这个作品几乎为预期的,但我必须yield from每天readwrite电话.我yield from从里面尝试过Connection:

def read(self, n_bytes : int = -1):
    data = yield from self.__in.read(n_bytes)
    return data
Run Code Online (Sandbox Code Playgroud)

但不是获取数据,而是获得类似的输出

<generator object StreamReader.read at 0x1109983b8>
Run Code Online (Sandbox Code Playgroud)

如果我打电话read,并write从多个地方,我宁愿不要重复yield from每次秒; 而是把它们放在里面Connection.我的最终目标是削减我的new_conection功能:

@asyncio.coroutine
def new_conection(stream_in, stream_out):
    conn = Connection(stream_in, stream_out)
    print(conn.read(4))
Run Code Online (Sandbox Code Playgroud)

小智 5

因为StreamReader.read是一个协程,你调用它的唯一选择是a)将它包装在一个Taskor中Future并通过一个事件循环运行它,b)await从定义的协程中运行它async def,或者c)yield from从一个定义为函数的协程中使用它与@asyncio.coroutine.

由于Connection.read从事件循环(通过协程new_connection)调用,您无法重用该事件循环来运行TaskFuturefor StreamReader.read:事件循环在它们已经运行时无法启动.您要么必须停止事件循环(灾难性的,可能无法正确执行),要么创建新的事件循环(混乱并且无法使用协同程序).这些都不是可取的,因此Connection.read需要成为协程或async函数.

另外两个选项(awaitasync def协程或yield from@asyncio.coroutine-decorated函数)大部分是等价的.唯一的区别是,async def并且await在Python 3.5中添加,因此对于3.4,使用yield from@asyncio.coroutine是唯一的选项(协同程序并且asyncio在3.4之前不存在,因此其他版本无关紧要).就个人而言,我更喜欢使用async defawait,因为定义协同程序async def比装饰器更清晰,更清晰.

简而言之:拥有Connection.readnew_connection成为协同程序(使用装饰器或async关键字),并在调用其他协程(in 和in )时使用await(或).yield fromawait conn.read(4)new_connectionawait self.__in.read(n_bytes)Connection.read