实现异步i

2024-05-16 05:52:54 发布

您现在位置:Python中文网/ 问答频道 /正文

PEP-492我试图实现一个异步迭代器,这样我就可以做例如

async for foo in bar:
    ...

下面是一个简单的例子,与文档中的例子类似,它对实例化和异步迭代进行了非常基本的测试:

import pytest

class TestImplementation:
    def __aiter__(self):
        return self
    async def __anext__(self):
        raise StopAsyncIteration


@pytest.mark.asyncio  # note use of pytest-asyncio marker
async def test_async_for():
    async for _ in TestImplementation():
        pass

但是,当我执行测试套件时,我看到:

=================================== FAILURES ===================================
________________________________ test_async_for ________________________________

    @pytest.mark.asyncio
    async def test_async_for():
>       async for _ in TestImplementation():
E       TypeError: 'async for' received an invalid object from __aiter__: TestImplementation

...: TypeError
===================== 1 failed, ... passed in 2.89 seconds ======================

为什么我的TestImplementation看起来无效?据我所知符合协议:

  1. An object must implement an __aiter__ method ... returning an asynchronous iterator object.
  2. An asynchronous iterator object must implement an __anext__ method ... returning an awaitable.
  3. To stop iteration __anext__ must raise a StopAsyncIteration exception.

最新发布的Python版本(3.5.1)、py.test(2.9.2)和pytest-asyncio(0.4.1)都会失败。


Tags: intestselfanasyncioforasyncobject
2条回答

如果你读a little further down the documentation它提到(强调我的):

PEP 492 was accepted in CPython 3.5.0 with __aiter__ defined as a method, that was expected to return an awaitable resolving to an asynchronous iterator.

In 3.5.2 (as PEP 492 was accepted on a provisional basis) the __aiter__ protocol was updated to return asynchronous iterators directly.

因此,对于3.5.2(2016/6/27版)之前的版本,文档与如何编写工作异步迭代器有些不协调。3.5.0和3.5.1的固定版本如下:

class TestImplementation:
    async def __aiter__(self):
  # ^ note
        return self
    async def __anext__(self):
        raise StopAsyncIteration

这是在结束bug #27243时引入的,在data model documentation中更清楚一些,这也建议了一种编写向后兼容代码的方法。

异步迭代器已在Python3.6中实现-请参见PEP-525

那么,为了使用async for,您根本不需要执行测试。您只需使用yield(示例取自PEP-525):

async def ticker(delay, to):
    """Yield numbers from 0 to `to` every `delay` seconds."""
    for i in range(to):
        yield i
        await asyncio.sleep(delay)

然后可以按预期使用async for

async for i in ticker(1, 10):                                                                     
    print(f'Tick #{i}')

相关问题 更多 >