When an object should work in a for loop, its __iter__ method must return an iterator. Python’s iter() built-in invokes __iter__, and the loop then requests values from the returned object until it raises StopIteration. section 10.0
A simple iterator
class LoudIterator: def __init__(self, data): self.data = data self.index = 0 def __iter__(self): return self def __next__(self): if self.index >= len(self.data): raise StopIteration value = self.data[self.index] self.index += 1 return value for item in LoudIterator('abc'): print(item) Here, the object is both iterable and its own iterator, so returning self is appropriate. __next__ stores progress on the object, returns one value at a time, and signals completion with StopIteration. section 10.0 section 10.2
Choose what __iter__ returns according to the object’s intended reuse behavior:
- Return self when the object represents one advancing iterator and repeated iteration should continue from its current state. section 10.2
- Return a fresh helper iterator when the object should support separate passes, such as iterating over the same data twice. section 10.2
- Return a generator or generator expression when it provides the desired iteration logic without a separate iterator class. section 10.5 section 10.7
class Circle():
def __init__(self, data, max_times):
self.data = data
self.max_times = max_times
def __iter__(self):
n = len(self.data)
return (self.data[x % n] for x in range(self.max_times))
A compact Circle implementation shows __iter__ returning a generator expression rather than self or a helper-class instance. section 10.7
The key design question is not whether __iter__ must return self, but whether it returns any iterator with the required behavior. That choice determines whether iteration is single-use, independently repeatable, bounded, or generated. section 10.7

