Python中基于yield的协程与使用@asyncio.coroutine和@types.coroutine装饰器的协程有何区别?

3
我一直在尝试理解异步编程,特别是在Python中。我知道asyncio是建立在事件循环之上的,它调度协程的执行,但我读到了几种不同的定义协程的方法,我不明白它们之间的关系。
我阅读了this article以获取更多背景信息。虽然它涵盖了我提到的四种协程类型,但它并没有完全描述它们之间的区别。在没有任何外部模块的情况下,可以使用yield作为等号右侧的表达式来创建协程,然后通过.send()输入数据。然而,使用@asyncio.coroutine@types.coroutine装饰器的代码示例从未使用过.send()。该文章中的代码示例如下:
# Coroutine using yield as an expression
def coro():
    hello = yield "Hello"
    yield hello
c = coro()
print(next(c), end=" ")
print(c.send("World")) # Outputs Hello World

# Asyncio generator-based coroutine
@asyncio.coroutine
def display_date(num, loop):
    end_time = loop.time() + 50.0
    while True:
        print("Loop: {} Time: {}".format(num, datetime.datetime.now()))
        if (loop.time() + 1.0) >= end_time:
            break
        yield from asyncio.sleep(random.randint(0, 5))

# Types generator-based coroutine
@types.coroutine
def my_sleep_func():
    yield from asyncio.sleep(random.randint(0, 5))

# Native coroutine in Python 3.5+
async def display_date(num, loop, ):
    end_time = loop.time() + 50.0
    while True:
        print("Loop: {} Time: {}".format(num, datetime.datetime.now()))
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(random.randint(0, 5))

我的问题是:
  1. yield 协程与 typesasyncio 装饰的协程有什么关系,以及何时使用 .send() 功能?
  2. 装饰器为未装饰的基于生成器的协程添加了哪些功能?
  3. @asyncio.coroutine@types.coroutine 装饰器有何不同?我阅读了 this answer 以尝试理解这一点,但这里提到的唯一区别是如果没有 yield 语句,types 协程会像子例程一样执行。还有其他区别吗?
  4. 这些基于生成器的协程在功能和实现上与最新的本机 async/await 协程有何不同?

yield 的意图是将值“发送”到使用您的生成器的任何地方。发送回您的生成器的内容完全取决于处理生成器的代码。在协程的情况下,您的生成器由某种事件循环处理(它会为您调用.send),并且您可以保证传递到生成器中的值直接从您传递出去的值产生,因此“await”是协程更直观的单词。 - Tadhg McDonald-Jensen
@TadhgMcDonald-Jensen asyncio.coroutine和types.coroutine装饰器在目的和功能上有何区别? - Rohan
我其实不知道,所以我没有发布答案,我会查看源代码,看看能否为您找到一个满意的答案。 - Tadhg McDonald-Jensen
在这里,@asyncio.coroutine@types.coroutine之间有一个重要的区别,请参考:https://dev59.com/o1kS5IYBdhLWcg3wl32I#49477233 - Amir Kirsh
1个回答

1
你可能会笑,我看了一下 asyncio.coroutine 的源代码,发现它使用了types.coroutine(任何带有#!>的注释都是我添加的)
def coroutine(func):
    """Decorator to mark coroutines...."""
 #!> so clearly the async def is preferred.
    warnings.warn('"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead',
                  DeprecationWarning,
                  stacklevel=2)
    if inspect.iscoroutinefunction(func):
 #!> since 3.5 clearly this is returning something functionally identical to async def.
        # In Python 3.5 that's all we need to do for coroutines
        # defined with "async def".
        return func 

    if inspect.isgeneratorfunction(func):
        coro = func
    else:
        #!> omitted, makes a wrapper around a non generator function.
#!> USES types.coroutine !!!!
    coro = types.coroutine(coro)
    if not _DEBUG:
        wrapper = coro
    else:
        #!> omitted, another wrapper for better error logging.

    wrapper._is_coroutine = _is_coroutine  # For iscoroutinefunction().
    return wrapper

所以我认为这只涉及历史问题,asynciotypes存在时间更长,因此最初的处理是在这里完成的,然后当types出现时,真正的包装器被移动到那里,而asyncio继续具有一些额外的包装处理。但归根结底,两者都只是为了模仿async def的行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接