PYTHON
Create Custom Iterables with `__iter__` and `__next__`
Learn to implement Python's iterator protocol (`__iter__` and `__next__`) to make your custom classes iterable, allowing them to be used directly in `for` loops and other iterable contexts.
class ReverseSequence:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
# This method must return an iterator object.
# In this case, 'self' is the iterator.
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
# Demonstrate usage
my_list = [10, 20, 30, 40, 50]
reversed_list = ReverseSequence(my_list)
print("Iterating through custom reversed sequence:")
for item in reversed_list:
print(item)
print("
Demonstrating re-iteration (requires new instance or reset):")
# A new instance is needed to re-iterate if __iter__ returns self
# or if the iterator is not resettable.
reversed_list_2 = ReverseSequence(my_list)
print(next(reversed_list_2)) # 50
print(next(reversed_list_2)) # 40
# A more common pattern for iterables (not iterators)
class MyCollection:
def __init__(self, items):
self._items = list(items)
def __iter__(self):
# This makes MyCollection an iterable.
# It returns a *new* iterator each time it's called.
return iter(self._items) # Delegate to list's iterator
print("
Iterating through a standard collection pattern:")
collection = MyCollection([1, 2, 3])
for x in collection:
print(x)
for x in collection: # Can iterate again
print(f"Again: {x}")
How it works: Python's iteration protocol relies on two methods: `__iter__` and `__next__`. When `iter(obj)` is called (e.g., implicitly by a `for` loop), `obj.__iter__()` is invoked and must return an iterator object. The iterator object then defines `__next__()`, which returns the next item in the sequence. When there are no more items, `__next__()` must raise `StopIteration`. This snippet demonstrates how to create a custom class (`ReverseSequence`) that acts as both an iterable and its own iterator, allowing it to be directly used in `for` loops. It also shows a more common pattern where `__iter__` returns a new iterator (e.g., by delegating to an underlying collection's iterator) to allow multiple independent iterations. This is crucial for building custom data structures that need to expose their contents in an iterable fashion.