代码之家  ›  专栏  ›  技术社区  ›  siebz0r ecatmur

wait\uuuu是否需要是生成器?

  •  4
  • siebz0r ecatmur  · 技术社区  · 6 年前

    我想实现一个等待已久的 __await__ “需要”是一个生成器。

    从…起 PEP-492 :

    具有 __等待__ 方法返回迭代器。

    。。。

    对象具有 __等待__ 方法在本PEP的其余部分中称为类未来对象。

    如果出现以下情况,则为类型错误 __等待__ 返回除迭代器以外的任何内容。

    根据我的经验,之前 await 是一份声明, yield from 与作为生成器实现的协同例程一起使用。现在python(我使用的是3.5)有异步方法,使用 async def 语法。因此,我认为 收益来自 语法为旧/已弃用。

    所以我请了口译员来看看这是如何/是否有效的:

    >>> class A:
    ...     def __await__(self):
    ...         yield from (asyncio.sleep(1).__await__())
    ...         return 'spam'
    ... 
    >>> a = A()
    >>> loop = asyncio.get_event_loop()
    >>> loop.run_until_complete(a)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib64/python3.5/asyncio/base_events.py", line 467, in run_until_complete
        return future.result()
      File "/usr/lib64/python3.5/asyncio/futures.py", line 294, in result
        raise self._exception
      File "/usr/lib64/python3.5/asyncio/tasks.py", line 240, in _step
        result = coro.send(None)
      File "/usr/lib64/python3.5/asyncio/tasks.py", line 585, in _wrap_awaitable
        return (yield from awaitable.__await__())
      File "<stdin>", line 3, in __await__
    AttributeError: 'generator' object has no attribute '__await__'
    

    看来是这样 asyncio.sleep 没有 __等待__ 方法使用这个也感觉很尴尬 收益来自 语法。

    所以我决定试试 async 语法,看看它是否有效:

    >>> class A:
    ...     async def __await__(self):
    ...         await asyncio.sleep(1)
    ...         return 'spam'
    ... 
    >>> a = A()
    >>> 
    >>> loop = asyncio.get_event_loop()
    >>> loop.run_until_complete(a)
    'spam'
    

    这似乎真的管用!所以现在我想知道 __等待__ 方法真的 需要 使用 收益来自 语法?


    编辑:添加间接级别时,因此在 等候 陈述问题变得明显:

    >>> async def test():
    ...     return await A()
    ... 
    >>> loop.run_until_complete(test())
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
        return future.result()
      File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
        raise self._exception
      File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
        result = coro.send(None)
      File "<stdin>", line 2, in test
    TypeError: __await__() returned a coroutine
    

    因此,它实际上需要返回这样一个生成器:

    class A:
        def __await__(self):
            yield from asyncio.sleep(1)
            return 'spam'    
    
    2 回复  |  直到 6 年前
        1
  •  5
  •   MisterMiyagi    5 年前

    为了在 await 表示 __await__ 不需要是发电机。但是,某些操作仅在以下情况下可用: __等待__ 支持发电机接口。

    也就是说,不可能 send 值或 throw 迭代器中的异常- __等待__ 。只有 None 可以“发送”到迭代器- __等待__ ,好像 generator.__next__ 已使用。


    让我们考虑一个简单的 Awaitable 从其 __等待__

    class Iter:
        """Basic iterator that yields the same value"""
        def __next__(self): return 1
        def __iter__(self): return self
    
    class IterAwait:
        """Awaitable that uses an iterator for __await__"""
        def __await__(self):
            return Iter()
    

    我们可以检查它们是否实现了所需的接口:

    >>> from collections.abc import Awaitable, Iterator, Generator
    >>> isinstance(IterAwait(), Awaitable)
    True
    >>> isinstance(IterAwait().__await__(), Iterator)
    True
    >>> isinstance(IterAwait().__await__(), Generator)
    False
    

    为了了解这与 等候 ,我们将其包装在一个协同程序中:

    async def iter_await():
        await IterAwait()
    

    我们执行的每个操作 iter_await 通过以下方式转发完整的协同程序/生成器接口 等候 到我们的迭代器- __等待__ 。这允许研究迭代器如何- __等待__ 接收信号:

    >>> test_iter = iter_await()
    >>> test_iter.send(3)         # 0. does it appear like a coroutine?
    TypeError: can`t send non-None value to a just-started coroutine
    >>> test_iter.send(None)      # 1. must initialise just-started coroutine
    1
    >>> test_iter.send(None)      # 2. repeatedly use the underlying iterator
    1
    >>> next(test_iter)           # 3. do we expose the iterator?
    TypeError: 'coroutine' object is not an iterator
    >>> test_iter.send(3)         # 4. can we send non-None values?
    AttributeError: 'Iter' object has no attribute 'send'
    >>> test_iter = iter_await()  # we just broke the coroutine...
    >>> test_iter.send(None)      # ...need a new one
    1
    >>> test_iter.throw(KeyError) # 4. can we throw Exceptions?
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in iter_await
    KeyError
    

    可以看出, 等候 可以处理迭代器- __等待__ ,但不转发所有操作。然而,有些是翻译的,有些是提前处理的。

    • 始终可以 .send(None) ,转换为裸体 __next__() 。(1、2)
    • 协同程序不会神奇地暴露 .__next__ (3) 无法翻译 .send 值为(4)。
    • 有可能 .throw 例外,但 等候 在协同程序的早期处理它。

    请注意 等候 使用 邮寄 方法可用。如果结果 __等待__ 机具 邮寄 但不是 反之亦然,则使用当前的功能。只有 __next__ 是强制性的。

        2
  •  3
  •   user4815162342    6 年前

    看来是这样 asyncio.sleep 没有 __await__ 方法

    没错,但它不一定要有一个可以等待的。这个 documentation 他说 __等待__ ,如果存在,则需要返回迭代器,而不是 await 仅适用于定义 __等待__ 。事实上,它明确记录了该论点 等候 可以是 其中之一 :

    • A. 本机协同程序 从本机协同例程函数返回的对象。

    • A. 基于生成器的协同程序 从用修饰的函数返回的对象 types.coroutine()

    • 具有 __等待__ 方法返回迭代器。

    • C中定义的对象,提供Python/C等效于 __等待__ 特殊方法。

    所以现在我想知道 __等待__ 方法确实需要是一个生成器,使用从语法中获得的收益?

    如果你真的有 __等待__ 方法,它确实需要返回迭代器。