14
how to use async for? (discuss.tchncs.de)

what does async for do? or, how do async iterators in general work, because I'm pretty sure async for just helps you iterate on an async iterator.

I think async iterators have a method __anext__() which "steps through the iterable"? I don't understand what stepping through means.

Also __anext__() returns an awaitable? what does that mean, especially if I have something like an AsyncIterator[Object], what does the awaitable have to do with the object?

top 2 comments
sorted by: hot top controversial new old
[-] logging_strict@programming.dev 2 points 1 week ago* (last edited 1 week ago)

Luckily the Python community is home to giants. sqlalchemy author is one such giant and he has something of interest to contribute to this conversation. But first i bore you with stuff we all already know.

from typing import AsyncIterator, Iterator
import contextlib

@contextlib.asynccontextmanager
async def somecoroutine() -> AsyncIterator[int]:
    teardownfcn = lambda : None
    something = 4
    try:
        yield something
    finally:
        teardownfcn()

@contextlib.contextmanager
def somefunction() -> Iterator[int]:
    teardownfcn = lambda : None
    something = 4
    try:
        yield something
    finally:
        teardownfcn()

async with somecoroutine() as stuff:
    print(f"some int {stuff!s}"

with somefunction() as stuff:
    print(f"some int {stuff!s}"

Although not a class, this pops out an AsyncIterator and shows sync equivalent.

From deep in the bowels of sqlalchemy

from sqlalchemy.ext.asyncio.base import GeneratorStartableContext, asyncstartablecontext

@asyncstartablecontext
async def somecoroutine() -> GeneratorStartableContext[int]:
    teardownfcn = lambda : print("Cookie monster says, yum yum yum")
    someiterable = [1,2,3]
    try:
        yield from someiterable
    except GeneratorExit:
        pass
    else:
        teardownfcn()

# no teardown
gen = await somecoroutine()
for thecount in gen:
    print(f"The Count says, {thecount!s}")

# teardown occurs
async with gen in somecoroutine():
    for thecount in gen:
        print(f"The Count says, {thecount!s}")

Should print

The Count says, 1
The Count says, 2
The Count says, 3
The Count says, 1
The Count says, 2
The Count says, 3
Cookie monster says, yum yum yum

The decorated function can be called either as async with fn(), or await fn(). This is decidedly different from what @contextlib.asynccontextmanager supports, and the usage pattern is different as well.

Above, GeneratorExit is caught if the function were used as an await. In this case, it's essential that the cleanup does not occur, so there should not be a finally block.

If GeneratorExit is not invoked, this means we're in __aexit__ and we were invoked as a context manager, and cleanup should proceed.

So instead of a class with __anext__ and __aiter__ an asyncstartablecontext with yield from could be a possible alternative.

[-] smq@discuss.tchncs.de 2 points 1 week ago

oh wow thanks!

this post was submitted on 15 Aug 2025
14 points (100.0% liked)

Python

7406 readers
1 users here now

Welcome to the Python community on the programming.dev Lemmy instance!

📅 Events

PastNovember 2023

October 2023

July 2023

August 2023

September 2023

🐍 Python project:
💓 Python Community:
✨ Python Ecosystem:
🌌 Fediverse
Communities
Projects
Feeds

founded 2 years ago
MODERATORS