Python __aiter__() and __anext__() Magic Methods

3.4/5 - (5 votes)


💡 Summary: Python’s __aiter__() and __anext__() methods are used to implement an asynchronous for loop (keywords: async for). In contrast to a normal (synchronous) for loop, an asynchronous for loop iterates over an asynchronous source.

  • __aiter__() returns an asynchronous iterator object (in many cases it’s simply a reference to itself: return self)
  • __anext__() returns the next element—could be obtained from an asynchronous source such as an asynchronous database. But doesn’t have to be!

We call these “Dunder Methods” for Double Underscore Methods” (also called “magic methods”). To get a list of all dunder methods with explanation, check out our dunder cheat sheet article on this blog.

Attention: A common misconception of async for is to assume that it iterates over the loop iterations in parallel. This is not the case. The async for iterates over an asynchronous source (e.g., if you iterate over database entries that may arrive asynchronously from user inputs). A synchronous for loop cannot do this.

Let’s dive into an example next!

Example Asynchronous Iterator

import asyncio

class Asynchronous_Iterator:
    ''' Iterate over an asynchronous source. n Iterations.'''
    def __init__(self, n):
        self.current = 0
        self.n = n

    def __aiter__(self):
        return self

    async def __anext__(self):
        await asyncio.sleep(1)
        print(f"get next element {self.current}")
        self.current += 1
        if self.current > self.n:
            raise StopAsyncIteration
        return self.current - 1

async def main():
    async for i in Asynchronous_Iterator(3):
        print(f"next element {i}")


get next element 0
next element 0
get next element 1
next element 1
get next element 2
next element 2
get next element 3
  • The AsynchronousIterator object implements the dunder methods __iter__() and __next__().
  • You use it in an asynchronous for loop like so: async for i in Asynchronous_Iterator(3)
  • The output shows that the iterations are performed synchronously, i.e., we first finish the execution of one loop iteration before starting the next!

💡 Note: Many people would expect the output to be like this—assuming Python first starts all loop iterations at the same time and then executing them in parallel:

get next element 0
get next element 1
get next element 2
get next element 3
next element 0
next element 1
next element 2

But this is not the case! Again, async for iterates over an asynchronous source, it is not a parallel for loop!

Background __iter__() and __next__()

The Python __iter__() method returns an iterator object. An iterator object is an object that implements the __next__() dunder method that returns the next element of the iterable object and raises a StopIteration error if the iteration is done.

The Python __next__ method returns an arbitrary element that represents the “next” element when you iterate over the object on which it is called. For example, if you iterate over my_object using for x in my_object, Python internally calls my_object.__next__() in each loop iteration to determine the next element.

Python next() -- A Simple Guide By Example

In the following example, you create a custom class Data and overwrite the __init__(), __iter__(), and __next__() methods so that you can create your own iterator over a Data object.

class Data:
    def __init__(self, data): = data # an iterable

    def __iter__(self):
        self.current_index = 0
        return self

    def __next__(self):
        if self.current_index < len(
            x =[self.current_index]
            self.current_index += 1
            return x
        raise StopIteration
  • __init__() initializes the data attribute that is expected to be an iterable.
  • __iter__() returns the iterator object — the one that implements the __next__() method. In our case, this is the Data object on which it is called itself. We initialize current_index with zero, so we start iterating with the first index of data.
  • __next__() returns the next value after one iteration. We increment the current_index attribute to keep track of the current index of the element in data.

Let’s create a Data object d and an iterator over the data object using the built-in iter() function (that internally calls __iter__())—and start iterating over the object using the built-in next() function (that internally calls __next__()):

d = Data([1, 'Alice', 42, 'finxter'])

# Create an iterator
iterator = iter(d)

# Dynamically generate the next values - iterate!

The output is as follows: The first four calls result in the expected elements of the data attribute, i.e., 1, 'Alice', 42, and 'finxter'. The fifth call of next() results in a StopIteration error because we have finished iterating over all elements.

Traceback (most recent call last):
  File "C:\Users\xcent\Desktop\", line 34, in <module>
  File "C:\Users\xcent\Desktop\", line 14, in __next__
    raise StopIteration


Where to Go From Here?

Enough theory. Let’s get some practice!

Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.

To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

You build high-value coding skills by working on practical coding projects!

Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?

🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!