在3.4和3.5之间的python中的协程,如何保持backwords兼容性?

ite*_*em4 11 python asynchronous python-3.x python-asyncio

我正在开发python聊天机器人框架asyncio.但是我看PEP-492并且有新的语法,async/ await最后它被接受了.

我喜欢async/ awaitsyntax,我想用它.但我担心3.4后缀兼容性.

如果我在代码中使用新语法,有人可以在3.4中使用它吗?

例如,我写了一些这样的代码,

import asyncio

class ChatBot:
    def __init__(self, loop):
        self.loop = loop

    async def connect(self):
        self.reader, self.writer = await asyncio.open_connect(HOST, PORT, loop=self.loop)

    async def read():
        return await self.reader.read()

    async def run(self):
        running = True
        while running:
            try:
                await self.connect()
                line = await self.read()
                if not line:
                    continue
                await self.parse(line)
            except BotInternalError as e:
                if e.stop:
                    running = False
                    break
            except:
                pass

    async def parse(self, msg):
        if msg.startswith('PING'):
            self.pong()
        elif msg.startswith('ERROR'):
            self.error()
        else:
            await self.some_work(msg)

    async def some_work(self, msg):
        # some looooooooong works
        self.send(msg)

    def send(self, msg):
        self.writer.write(msg)
Run Code Online (Sandbox Code Playgroud)

比,我可以在py35中使用它

loop = asyncio.get_event_loop()  # I don't know it really needed in py35.
bot = ChatBot(loop)
asyncio.run_until_complete(bot.run())
Run Code Online (Sandbox Code Playgroud)

但是,py34没有await语法.如果我在没有版本限制的PyPI上面上传源代码并且某人在py34上安装了它,它会正常工作吗?我怎么能保留它?

dan*_*ano 13

如果您需要在代码中支持Python 3.4,则需要使用旧的@asyncio.coroutine/ yield fromstyle语法.如果不运行3.5,就无法支持async/ awaitsyntax; 你将获得SyntaxError3.4或更低的编译时间.

这需要的新功能,你的优势唯一可以以向后兼容的方式做的就是各种添加__a*__方法到类在适当情况下(__aiter__,__aenter__,__aexit__等),使用yield from协程语法.这样,您的对象可以支持async with/ async for语句,因此运行Python 3.5的库的用户可以利用新功能.

例如,这个类可以async with在Python 3.4上运行时使用,但不会中断:

import asyncio

class Test:
    def __enter__(self):
        return self

    def __exit__(self, *args):
        print("arg")

    @asyncio.coroutine
    def __aenter__(self):
        yield from self.init_state()
        return self

    @asyncio.coroutine
    def init_state(self):
        yield from asyncio.sleep(2) # Pretend this is real initialization

    @asyncio.coroutine
    def __aexit__(self, *args):
        return self.__exit__(self, *args)
Run Code Online (Sandbox Code Playgroud)

在Python 3.5上:

import asyncio
from test import Test

async def main():
    print("entering with")
    async with Test() as t:
        print("in here")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)

在Python 3.4上

import asyncio
from test import Test

@asyncio.coroutine
def oldmain():
    print("entering with")
    with Test() as t:
        yield from t.init_state()
        print("in here")

loop = asyncio.get_event_loop()
loop.run_until_complete(oldmain())
Run Code Online (Sandbox Code Playgroud)

如果你正在编写一个使用的应用程序,这可能没有用asyncio,但是如果你正在开发一个供其他开发人员使用的库或框架,那么它值得做.

  • 当它出现在最新的Python版本中时,将`@asyncio.coroutine`结构描述为"旧"是很有趣的.:-) (7认同)