ite*_*em4 6 python asynchronous coroutine python-3.x python-asyncio
我想以异步方式运行我的代码.我应该装饰@asyncio.coroutine什么以及我应该yield from为异步操作调用什么?
在我的例子中,我有一些没有装饰器的示例代码.(简单的聊天机器人看起来像IRC)
import asyncio
class ChatBot:
    def __init__(self, loop):
        conn = asyncio.open_connection(HOST, PORT, loop=loop)
        self.reader, self.writer = yield from conn
    def send(self, msg):
        self.writer.write(msg)
    def read(self):
        msg = yield from self.reader.readline()
        return msg
    def run(self):
        while True:
            msg = self.read()
            self.parse(msg)
    def parse(self, msg):
        if msg.startswith('PING'):
            self.some_work(msg)
        elif msg.startswith('ERROR'):
            self.some_error()
        else:
            self.server_log(msg)
    def some_work(self, msg):
        # some work. It can call asynchronous function like I/O or long operation. It can use self.send().
    def some_error(self, msg):
        # some work. It can call asynchronous function like I/O or long operation. It can use self.send().
    def server_log(self, msg):
        # some work. It can call asynchronous function like I/O or long operation. It can use self.send().
loop = asyncio.get_event_loop()
bot = ChatBot(loop)
loop.run_until_complete(???)
loop.close()
我认为???是bot.run(),ChatBot.run必须装饰@asyncio.coroutine.那么,其他方法呢?我无法理解何时使用@asyncio.coroutine装饰器和调用方法yield from或asyncio.async.(我已经阅读了PEP-3156以了解asnycio.但我无法完全理解.)
dan*_*ano 12
@asyncio.coroutine装饰器如果你有一个需要用来yield from调用一个协同程序的函数,你应该用它来装饰它asyncio.coroutine.还要注意,协同程序通常(并非总是)是"病毒"的.一旦你添加yield from到一个函数它就变成了一个协程,而且通常(虽然不总是)调用该协程的任何函数也需要成为一个协程.
asyncio.async协同程序为什么不总是病毒?因为你实际上并不总是需要yield from用来调用一个协同程序.yield from如果你想调用一个协程并等待它完成,你只需要使用.如果你只想在后台启动协程,你可以这样做:
asyncio.async(coroutine())
coroutine一旦控制返回到事件循环,这将安排运行; coroutine在继续下一行之前,它不会等待完成.普通函数可以使用它来安排协程运行,而不必成为协程本身.
您还可以使用此方法同时运行多个coroutines.所以,想象你有这两个协程:
@asyncio.coroutine
def coro1():
   yield from asyncio.sleep(1)
   print("coro1")
@asyncio.coroutine
def coro2():
   yield from asyncio.sleep(2)
   print("coro2")
如果你有这个:
@asyncio.coroutine
def main():
    yield from coro1()
    yield from coro2()
    yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
1秒后,"coro1"将被打印.然后,在两秒钟之后(总共三秒),"coro2"将打印,五秒钟后程序将退出,使总运行时间为8秒.或者,如果您使用asyncio.async:
@asyncio.coroutine
def main():
    asyncio.async(coro1())
    asyncio.async(coro2())
    yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
这将"coro1"在一秒钟之后打印,"coro2"一秒钟之后,程序将在3秒后退出,总计运行时间为5秒.
因此遵循这些规则,您的代码需要如下所示:
import asyncio
class ChatBot:
    def __init__(self, reader, writer):
        # __init__ shouldn't be a coroutine, otherwise you won't be able
        # to instantiate ChatBot properly. So I've removed the code that
        # used yield from, and moved it outside of __init__.
        #conn = asyncio.open_connection(HOST, PORT, loop=loop)
        #self.reader, self.writer = yield from conn
        self.reader, self.writer = reader, writer
    def send(self, msg):
        # writer.write is not a coroutine, so you 
        # don't use 'yield from', and send itself doesn't 
        # need to be a coroutine.
        self.writer.write(msg)
    @asyncio.coroutine
    def read(self):
        msg = yield from self.reader.readline()
        return msg
    @asyncio.coroutine
    def run(self):
        while True:
            msg = yield from self.read()
            yield from self.parse(msg)
    @asyncio.coroutine
    def parse(self, msg):
        if msg.startswith('PING'):
            yield from self.some_work(msg)
        elif msg.startswith('ERROR'):
            yield from self.some_error()
        else:
            yield from self.server_log(msg)
    @asyncio.coroutine
    def some_work(self, msg):
        # some work. It can call asynchronous function like I/O or long operation. It can use self.send().
    @asyncio.coroutine
    def some_error(self, msg):
        # some work. It can call asynchronous function like I/O or long operation. It can use self.send().
    @asyncio.coroutine
    def server_log(self, msg):
        # some work. It can call asynchronous function like I/O or long operation. It can use self.send()
@asyncio.coroutine
def main(host, port):
    reader, writer = yield from asyncio.open_connection(HOST, PORT, loop=loop)
    bot = ChatBot(reader, writer)
    yield from bot.run()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
另外要记住的一点是 - yield from在函数前添加并不会神奇地使该调用无阻塞.也没有添加@asyncio.coroutine装饰器.如果函数实际上是直接或间接调用本机asyncio协同程序,它们使用非阻塞I/O并与asyncio事件循环集成,则它们只是非阻塞的.例如,您提到了进行REST API调用.为了使那些REST API调用不阻止事件循环,您需要使用aiohttp库,或asyncio.open_connection.使用类似requests或urllib将阻止循环的东西,因为它们没有与`asyncio集成.