🎓 A1Learner
🔍

Python Container Protocols

Container protocols are sets of dunder methods that make a class work like a built-in container.

Start with __len__

len() simply calls your __len__ method. Return the count of the inner list.

class Playlist:
    def __init__(self, name, songs):
        self.name = name
        self.songs = list(songs)

    def __len__(self):
        return len(self.songs)

mix = Playlist("Focus", ["Aurora", "Breeze", "Comet"])
print(len(mix))

Add __getitem__ for indexing

Python passes each index to __getitem__. Add this method to the Playlist class.

    def __getitem__(self, index):
        return self.songs[index]

Indexing and slicing now work

A slice arrives as one slice object. Passing it to the inner list returns a new list.

print(mix[0])
print(mix[-1])
print(mix[1:3])

What __getitem__ gives for free

This class has no __iter__ yet. Python still loops by asking for index 0, 1, 2, and so on.

for song in mix:
    print(song)

Add __contains__ for in

The in operator calls __contains__ when you define it. Add this method to the class.

    def __contains__(self, song):
        return song in self.songs

Membership checks read cleanly

Your method now answers the question directly. It no longer needs to scan by index.

print("Breeze" in mix)
print("Echo" in mix)

Delegate iteration to the inner list

Delegation means handing a job to an inner object. The list already iterates well, so __iter__ returns its iterator.

    def __iter__(self):
        return iter(self.songs)

Example

class Playlist:
    def __init__(self, name, songs):
        self.name = name
        self.songs = list(songs)

    def __len__(self):
        return len(self.songs)

    def __getitem__(self, index):
        return self.songs[index]

    def __contains__(self, song):
        return song in self.songs

    def __iter__(self):
        return iter(self.songs)

mix = Playlist("Focus", ["Aurora", "Breeze", "Comet"])
print(len(mix))
print(mix[1])
print(mix[0:2])
print("Comet" in mix)
for song in mix:
    print(song)

Key points

  • Sequence: an object you can count, index, and loop over in order
  • Protocol: a set of special methods that unlock built-in behavior
  • __getitem__: receives one index, or one slice object for a slice
  • Use iter(inner_list) inside __iter__ to delegate the looping work
  • Use __contains__ to answer in checks with your own logic

In this example, Playlist stores a name and a list of songs. __len__ returns the song count, so len(mix) prints 3. __getitem__ passes each index or slice to the inner list. mix[1] prints Breeze, and mix[0:2] prints a two-song list. __contains__ answers the in check, so "Comet" in mix is True. __iter__ returns the inner list iterator, so the loop prints each song.

Note

With only __getitem__, loops and in still work by counting indexes, which can be slow.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

A class defines __getitem__ with indexes starting at 0, but no __iter__. What happens in a for loop over it?