Container Dunders: __len__, __getitem__, and __iter__
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)In Python, you do not need to inherit from a rigid collection framework to create custom collections. By implementing a handful of dunder methods (__len__, __getitem__, __iter__), your class works seamlessly with len(), bracket indexing [], for loops, and in membership checks.
Supporting len() with __len__
__len__ is the special method that returns how many items an object holds. len(obj) calls it, and an object with length 0 counts as False.
Implementing __len__ connects your class directly to Python’s built-in len() function.
class Deck: def __init__(self): self._cards = ["Ace", "King", "Queen", "Jack"]
def __len__(self): return len(self._cards)
d = Deck()print(len(d)) # 4Example 1 · An empty object is falsy
class Cart: def __init__(self, items): self.items = items
def __len__(self): return len(self.items)
cart = Cart([])if not cart: print("Cart is empty")cart.items.append("Notebook")print(len(cart), bool(cart))Cart is empty 1 True
What this code does
- With
__len__defined,len(cart)works and an empty cart counts asFalse. - So
if not cart:is the natural Python check, with nolen(cart) == 0.
__len__ must return a non-negative integer.
Bracket Indexing with __getitem__
__getitem__ is the special method behind obj[key]. It receives the index, a slice or any key, and returns the matching item.
Implementing __getitem__ allows accessing elements with brackets (d[0]).
class Deck: def __init__(self): self._cards = ["Ace", "King", "Queen", "Jack"]
def __getitem__(self, index): return self._cards[index]
d = Deck()print(d[0]) # Aceprint(d[-1]) # Jackprint(d[1:3]) # ['King', 'Queen'] (Automatic slicing!)Example 2 · Slices come free
class Report: def __init__(self, rows): self._rows = rows
def __getitem__(self, index): return self._rows[index]
r = Report([10, 20, 30, 40, 50])print(r[0], r[-1])print(r[1:4])print(r[::-1])10 50 [20, 30, 40] [50, 40, 30, 20, 10]
What this code does
- The index is passed straight to the list, so negative indexes and slices behave like list ones.
r[::-1]hands a slice object to__getitem__.
Because _cards is a list, delegating self._cards[index] automatically supports negative indices and slicing!
Free Iteration and Membership
Remarkably, simply having __len__ and __getitem__ automatically gives your class for loop iteration and in membership testing for free!
class Deck: def __init__(self): self._cards = ["Ace", "King", "Queen", "Jack"]
def __getitem__(self, index): return self._cards[index]
d = Deck()
# Python asks for index 0, 1, 2... until IndexError is raisedfor card in d: print(card)
print("Queen" in d)Example 3 · A team that behaves like a list
class Team: def __init__(self, members): self.members = members def __len__(self): return len(self.members) def __getitem__(self, i): return self.members[i]
t = Team(["Aarav", "Bhavna", "Chetan"])print("Size:", len(t))print("Captain:", t[0])print("Has Chetan:", "Chetan" in t)Size: 3 Captain: Aarav Has Chetan: True
What this code does
len(t)calls__len__.t[0]calls__getitem__."Chetan" in titerates through__getitem__automatically.
Python’s protocol system is elegantly designed: __getitem__ acts as a fallback iterator.
Explicit Iteration with __iter__
Iterable is any object that can be looped over with for. It has __iter__, which returns an iterator that hands out one item at a time.
For explicit iteration control or non-indexed collections, implement __iter__ returning an iterator.
class Pipeline: def __init__(self, steps): self.steps = steps
def __iter__(self): return iter(self.steps)
for step in Pipeline(["fetch", "clean", "save"]): print(step)Example 4 · A generator makes __iter__ short
class Roster: def __init__(self, people): self.people = people
def __iter__(self): for name, active in self.people: if active: yield name
staff = Roster([("Aarav", True), ("Bhavna", False), ("Chetan", True)])print(list(staff))print([n.upper() for n in staff])['Aarav', 'Chetan'] ['AARAV', 'CHETAN']
What this code does
yieldturns__iter__into a generator, so each loop gets a fresh run.- That is why the same object can be looped over twice.
Delegating to iter(...) is concise and fast.
Next step
Check your understanding
11 quick questions