class Inventory:
    def __init__(self):
        self.slots = []

    def add(self, item):
        self.slots.append(item)

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

    def __contains__(self, item):
        return item in self.slots

    def __iter__(self):
        for item in self.slots:
            yield item

__len__
Gives your object a collection length.

__contains__
For checking membership (e.g. using in).

__iter__
Makes your object iterable.

yield 
lets you send data back out of a function — like return — without ending the execution of the function.

Since we’re just returning values from an iterable, we can use yield from to skip the entire for loop:

def __iter__(self):
    for item in self.slots:
        yields item

This does the same thing previously written

Checking Membership in Python
  1. If contains doesn’t exist, Python tries two other magic methods; iterand getitem — These two methods aren’t meant for checking membership but they can be used for it.
  2. If iter exists the object is considered to be iterable, and Python loops through it trying to find what you have asked for.
  3. If iter doesn’t exist, Python tries to use getitem, which is the method that lets you pluck things out of a collection with an index or a key.