Python 异步运行或运行\u直到\u完成

Python 异步运行或运行\u直到\u完成,python,python-asyncio,Python,Python Asyncio,我以一种非常基本的方式为应用程序使用asyncio。通过查看internet上的大多数教程(甚至是官方文档),我发现它们使用了get\u event\u loop()和循环 导入异步IO 异步定义所说的(什么,何时): 等待asyncio.sleep(何时) 打印(什么) loop=asyncio.get\u event\u loop() 循环。运行_直到_完成(说('helloworld',1)) loop.close() 但在这本书中,我们可以读到: 应用程序开发人员通常应该使用高级异步I

我以一种非常基本的方式为应用程序使用
asyncio
。通过查看internet上的大多数教程(甚至是官方文档),我发现它们使用了
get\u event\u loop()
循环

导入异步IO
异步定义所说的(什么,何时):
等待asyncio.sleep(何时)
打印(什么)
loop=asyncio.get\u event\u loop()
循环。运行_直到_完成(说('helloworld',1))
loop.close()
但在这本书中,我们可以读到:

应用程序开发人员通常应该使用高级异步IO函数,例如,并且应该很少需要引用循环对象或调用其方法。本节主要针对低级别代码、库和框架的作者,他们需要更好地控制事件循环行为

我发现它更干净、更易于使用,但它只适用于Python3.7+。因此,在这里我必须做出选择,是使用Python3.7+和
run()
,还是使其与Python3.6兼容并使用事件循环。你将如何处理这个问题?是否有一种简单的方法使其向后兼容Python 3.6?在Python3.7成为一个通用版本之前,我是否应该首先检查Python版本,并在此基础上使用一种或另一种方法

有没有一种简单的方法使[代码使用
asyncio.run
]向后兼容Python 3.6

您可以实现asyncio的一个简单替代品。运行
并在较旧的Python版本上调用它:

import asyncio, sys, types

def run(coro):
    if sys.version_info >= (3, 7):
        return asyncio.run(coro)

    # Emulate asyncio.run() on older versions

    # asyncio.run() requires a coroutine, so require it here as well
    if not isinstance(coro, types.CoroutineType):
        raise TypeError("run() requires a coroutine object")

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        return loop.run_until_complete(coro)
    finally:
        loop.close()
        asyncio.set_event_loop(None)

与只使用
loop.run_直到_complete()
相比,这种方法的优势在于,即使在较旧的Python版本上,您也可以使用与新的
asyncio.run
相近的语义来执行代码。(例如,您将始终在新创建的事件循环上运行。)放弃对3.7版之前Python的支持将非常简单,只需删除
run
垫片并直接调用
asyncio.run

通过从asyncio.runners.py复制代码,就可以复制
asyncio.run
。下面的一个来自Python 3.8

from asyncio import coroutines, events, tasks


def run(main, *, debug=False):
    """Execute the coroutine and return the result.

    This function runs the passed coroutine, taking care of
    managing the asyncio event loop and finalizing asynchronous
    generators.

    This function cannot be called when another asyncio event loop is
    running in the same thread.

    If debug is True, the event loop will be run in debug mode.

    This function always creates a new event loop and closes it at the end.
    It should be used as a main entry point for asyncio programs, and should
    ideally only be called once.

    Example:

        async def main():
            await asyncio.sleep(1)
            print('hello')

        asyncio.run(main())
    """
    if events._get_running_loop() is not None:
        raise RuntimeError(
            "asyncio.run() cannot be called from a running event loop")

    if not coroutines.iscoroutine(main):
        raise ValueError("a coroutine was expected, got {!r}".format(main))

    loop = events.new_event_loop()
    try:
        events.set_event_loop(loop)
        loop.set_debug(debug)
        return loop.run_until_complete(main)
    finally:
        try:
            _cancel_all_tasks(loop)
            loop.run_until_complete(loop.shutdown_asyncgens())
        finally:
            events.set_event_loop(None)
            loop.close()


def _cancel_all_tasks(loop):
    to_cancel = tasks.all_tasks(loop)
    if not to_cancel:
        return

    for task in to_cancel:
        task.cancel()

    loop.run_until_complete(
        tasks.gather(*to_cancel, loop=loop, return_exceptions=True))

    for task in to_cancel:
        if task.cancelled():
            continue
        if task.exception() is not None:
            loop.call_exception_handler({
                'message': 'unhandled exception during asyncio.run() shutdown',
                'exception': task.exception(),
                'task': task,
            })

如果你要编写更复杂的代码,在两个版本上都能工作,在新版本上更简单的代码,并且你会在它们之间动态切换……那么简单地使用一个更复杂的代码,在两个版本上都能工作,不是更容易吗?@deceze是的,也许这是最好的选择,我想对此有一个看法,如果要使它兼容,我想知道哪种方法是最好的so@deceze在较旧的Python版本上模拟
asyncio.run
,并不困难,而且您可以在
asyncio.run
设置的条件下测试代码,即在新创建的事件循环上。很好!这看起来很棒!我会试试这个:)谢谢!只是代码中的一个小问题,条件应该是
sys.version\u info>=(3,7)
。谢谢你的帮助!:)超级的!谢谢!