我有一个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()
在服务器端,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)
在客户端,写出4个字节。
@asyncio.coroutine
def client(loop):
...
conn = Connection(stream_in, stream_out)
yield from conn.write(b'test')
此功能几乎与预期一致,但每次yield from
和read
呼叫都必须write
。我在yield from
内尝试了Connection
:
def read(self, n_bytes : int = -1):
data = yield from self.__in.read(n_bytes)
return data
但是我没有获取数据,而是得到了像
这样的输出<generator object StreamReader.read at 0x1109983b8>
如果我从多个地方拨打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))
答案 0 :(得分:5)
由于StreamReader.read
is a coroutine,您调用它的唯一选择是a)将其包装在Task
或Future
并通过事件循环运行,b)await
ing它来自使用async def
定义的协同程序,或c)使用yield from
将其与来自定义为@asyncio.coroutine
装饰的函数的协程一起使用。
由于从事件循环调用Connection.read
(通过协程new_connection
),您无法重用该事件循环来为{Task
或Future
运行{ {1}}:event loops can't be started while they're already running。您要么stop the event loop(灾难性的,可能无法正确执行)或create a new event loop(混乱并且无法使用协程的目的)。这些都不可取,因此StreamReader.read
需要是协程或Connection.read
函数。
其他两个选项(async
协程中的await
或async def
- 装饰函数中的yield from
)大致相当。唯一的区别是async def
and await
were added in Python 3.5,因此对于3.4,使用@asyncio.coroutine
和yield from
是唯一的选项(协程和@asyncio.coroutine
在3.4之前不存在,所以其他版本是无关紧要的)。就个人而言,我更喜欢使用asyncio
和async def
,因为使用await
定义协同程序比使用装饰器更清晰,更清晰。
简而言之:让async def
和Connection.read
成为协同程序(使用装饰器或new_connection
关键字),然后使用async
(或await
)在调用其他协程时(yield from
中的await conn.read(4)
和new_connection
中的await self.__in.read(n_bytes)
)。
答案 1 :(得分:1)
我在第620行发现StreamReader source code的一大块实际上是函数使用的完美示例。
在我之前的回答中,我忽略了这样一个事实,即self.__in.read(n_bytes)
不仅仅是一个协程(我应该知道它是来自asyncio
模块XD),但它产生了一个结果在线。所以它实际上是一个发电机,你需要从它产生。
从源代码中借用这个循环,你的read函数看起来像这样:
def read(self, n_bytes : int = -1):
data = bytearray() #or whatever object you are looking for
while 1:
block = yield from self.__in.read(n_bytes)
if not block:
break
data += block
return data
因为self.__in.read(n_bytes)
是一个生成器,所以你必须继续从它产生,直到它产生一个空结果来表示读取结束。现在你的read函数应该返回数据而不是生成器。您不必从这个版本的conn.read()
中获益。