Container Emulation
One of the most powerful aspects of Python is its “Plug-and-Play” nature. You don’t have to inherit from a special List class to make your object behave like a list. By implementing a handful of “Magic Methods,” your custom object can support indexing, slicing, iteration, and the in operator.
This is known as the Container Protocol.
1. The Core Three: Length, Getting, and Setting
Section titled “1. The Core Three: Length, Getting, and Setting”__len__(self)
Section titled “__len__(self)”Called when you run len(obj). Should return an integer.
__getitem__(self, key)
Section titled “__getitem__(self, key)”Called when you run obj[key].
- For a Sequence (list-like), the key is an integer or a slice.
- For a Mapping (dict-like), the key is an immutable object.
__setitem__(self, key, value)
Section titled “__setitem__(self, key, value)”Called when you run obj[key] = value.
class Box: def __init__(self): self._items = {}
def __len__(self): return len(self._items)
def __getitem__(self, key): return self._items.get(key, "Empty")
def __setitem__(self, key, value): self._items[key] = value
b = Box()b["tool"] = "Hammer"print(b["tool"]) # Hammerprint(len(b)) # 12. Membership & Iteration
Section titled “2. Membership & Iteration”__contains__(self, item)
Section titled “__contains__(self, item)”Called when you use the in operator.
if "Hammer" in b: # Calls b.__contains__("Hammer") ...__iter__(self)
Section titled “__iter__(self)”Called when you use a for loop. It should return an Iterator object.
3. Slicing Support
Section titled “3. Slicing Support”To support slicing (obj[1:5]), you don’t need a new method. Python passes a slice object into your __getitem__ method instead of an integer.
class CustomList: def __init__(self, data): self.data = data
def __getitem__(self, index): if isinstance(index, slice): print(f"Slicing from {index.start} to {index.stop}") return self.data[index] return self.data[index]
c = CustomList([10, 20, 30, 40])print(c[1:3]) # Output: [20, 30]4. Under the Hood: The “Collection” Abstract Base Classes
Section titled “4. Under the Hood: The “Collection” Abstract Base Classes”If you are building a production-grade container, you shouldn’t just guess which methods to implement. Python provides Abstract Base Classes in the collections.abc module (like Sequence, MutableSequence, Mapping).
By inheriting from these, you get many methods (like __contains__, __iter__, and __reversed__) for free, provided you implement the core ones (__getitem__ and __len__).
5. Summary Table: The Container Protocol
Section titled “5. Summary Table: The Container Protocol”| Operation | Magic Method |
|---|---|
len(obj) | __len__ |
obj[k] | __getitem__ |
obj[k] = v | __setitem__ |
del obj[k] | __delitem__ |
item in obj | __contains__ |
for x in obj | __iter__ |
reversed(obj) | __reversed__ |