aiter()

Exactly what iter is for ordinary iterables, moved into the async world. Note it is NOT awaited — only anext is.

Built-in functionPython 3.10+
Common call
it = aiter(stream)
Returns
an async iterator — not a coroutine, so no await here
Replaces
calling stream.__aiter__() by hand
Watch out
aiter() is sync; anext() is what you await
aiter(async_iterableasync_iterableAn object implementing __aiter__ — an async generator, an async stream, or a class defining it.type: async iterable · required)
async iterator

Parameters

NameTypeRequiredDescription
async_iterableasync iterableyesAn object implementing __aiter__ — an async generator, an async stream, or a class defining it.

Return value

async iteratorThe object returned by async_iterable.__aiter__(). Raises TypeError if the argument does not implement __aiter__.

Examples

1. Get an iterator
it = aiter(stream)
Returns
an async iterator
2. Step it manually
it = aiter(stream) first = await anext(it)
Returns
the first item
3. No await on aiter
it = aiter(stream) # not awaited
Returns
returns immediately
4. What async for does
async for x in stream: ...
Returns
calls aiter then anext repeatedly
5. Wrong type
aiter([1, 2, 3])
Returns
TypeError: 'list' object is not an async iterable
6. Idempotent
it = aiter(stream) aiter(it) is it
Returns
True for a well-behaved iterator

Pitfalls

1. aiter is not awaited, anext is
The asymmetry catches almost everyone. __aiter__ returns the iterator synchronously; only __anext__ is a coroutine. Awaiting aiter gives a TypeError about a non-awaitable.
Awaited wrongly
it = await aiter(stream)
TypeError: object async_generator can't be used in 'await' expression
Await only anext
it = aiter(stream)
item = await anext(it)
correct
2. A plain iterable is not an async iterable
aiter needs __aiter__, which lists, generators and files do not have. There is no automatic bridge — wrap it in an async generator if you need one.
Sync list rejected
aiter([1, 2, 3])
TypeError: 'list' object is not an async iterable
Wrap it
async def to_async(xs):
    for x in xs:
        yield x

aiter(to_async([1, 2, 3]))
an async iterator
3. No two-argument form
iter(callable, sentinel) has a second form; aiter does not. Passing two arguments is a TypeError rather than a sentinel-driven loop.
No sentinel form
aiter(read_chunk, b"")
TypeError: aiter expected 1 argument, got 2
Loop explicitly
while (chunk := await read_chunk()) != b"":
    ...
same effect, written out
4. Python 3.10 and newer only
Async iteration itself dates from 3.5, but the aiter and anext builtins only arrived in 3.10. On older versions call the dunder directly.
Fails on 3.9
aiter(stream)
NameError: name 'aiter' is not defined
Call the dunder
it = stream.__aiter__()
works from 3.5

When to use

Use it
  • Stepping an async stream manually rather than with async for
  • Writing generic code that must accept any async iterable
  • Implementing helpers such as an async version of zip or islice
Reach for something else
  • A simple loop → async for is clearer and handles the protocol for you
  • Synchronous iterables → iter
  • Supporting Python 3.9 or older without a fallback

Notes

Complexity
O(1) — one call to __aiter__
Return
An async iterator; calling aiter on one should return it unchanged
CPython impl
Python/bltinmodule.c :: builtin_aiter
Memory
No allocation beyond whatever __aiter__ creates
Thread-safe
Bound to its event loop — do not share an async iterator across loops

FAQ

Because getting an iterator does no I/O — it just hands back an object. Only advancing it can block, which is why __anext__ is the coroutine and __aiter__ is an ordinary method.

it = aiter(stream)          # sync
item = await anext(it)      # async

History

3.5
Async iteration protocol introduced by PEP 492 (__aiter__ and __anext__).
3.10
aiter and anext added as builtins, mirroring iter and next.