python 协程在 3.4 和 3.5 之间,如何保持向后兼容性?

Coroutine in python between 3.4 and 3.5, How can I keep backwords compatibility?

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

我喜欢 async/await 语法,我想使用它。但我担心 3.4 backwords 兼容性。

如果我在我的代码中使用新语法,有人可以在 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)

然后,我可以在 py35

中使用这个源代码
loop = asyncio.get_event_loop()  # I don't know it really needed in py35.
bot = ChatBot(loop)
asyncio.run_until_complete(bot.run())

但是,py34 没有 await 语法。如果我在没有版本限制的情况下在 PyPI 上上传以上源代码并且有人将其安装在 py34 上,它会正常工作吗?我怎样才能保留它?

如果您需要在代码中支持 Python 3.4,则需要使用旧的 @asyncio.coroutine/yield from 样式语法。如果没有 运行ning 3.5,就无法支持 async/await 语法;你会在 3.4 或更低版本的编译时得到一个 SyntaxError

唯一利用您可以以向后兼容的方式执行的新功能的方法是将各种__a*__方法添加到您的classes 在适当的地方(__aiter____aenter____aexit__ 等),使用 yield from 协程语法。这样,您的对象可以支持 async with/async for 语句,这样您的库 运行ning Python 3.5 的用户就可以利用新功能。

例如,这个 class 可以与 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)

在 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())

在 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())

如果您正在编写使用 asyncio 的应用程序,这可能没有用,但如果您正在开发旨在供其他开发人员使用的库或框架,则值得这样做。