Building iterable objects in Python

Using __len__ & __getItem__

Shreejit Rajbanshi
Devil is in the Details
3 min readNov 20, 2022

--

Photo by Tristan Gevaux on Unsplash

Python data model is different from other programming languages such that it calls special methods to perform basic object operations, often triggered by special syntax.

Those method names always lead, and trail with double underscores (i.e __len__).

There are many special methods, __init__ being the most common one of them. We, as programmers, don’t invoke them directly. But adding them to our class allows us to append some of python’s core functionalities with ease.

Step 1 : __len__ method

Presuming hypothetically, we want to create a Library object housing a list of books, the object and its functions would look something like this:

class Library: 
def __init__(self):
self._books = [
"Atomic Habits",
"Shoe Dog",
"Deep Work",
"Zero to One"]

def book_count(self):
''' Custom function returning the no of books '''
return len(self._books)
>>> library = Library()
>>> library.book_count()
4

The implementation is correct. But as an iterable class, a better approach would be allowing users to pass the object into the len() function. Having a standardized function means users of our class don’t have to memorize arbitrary function name or search for it, specially for standard operations like this.

Adding __len__ instead of our custom function and we have done it.

class Library: 
def __init__(self):
self._books = [
"Atomic Habits",
"Shoe Dog",
"Deep Work",
"Zero to One"]

def __len__(self):
return len(self._books)
>>> library = Library()
>>> len(library) # Returns 4

Step 2: __getItem__ method

With our approach to compute length finalized, it gets better. [] operator used for accessing based on index, delegates the task of finding the item to __getitem__ function.

class Library: 
def __init__(self):
self._books = [
"Atomic Habits",
"Shoe Dog",
"Deep Work",
"Zero to One"]

def __len__(self):
''' Custom function returning the no of books '''
return len(self._books)

def __getitem__(self, position):
return self._books[position]

Rather than trying to reinvent the wheel we just inherited the array like behaviour into our own objects.

>>> library = Library()
>>> library[1]
Shoe Dog

Should we create a method to pick a random book? No need. Python already has a function to get a random item from a sequence: rondom.choice. We can now use it on our object.

>>> from random import choice
>>> choice(library)
Deep Work

Slicing operation are also readily available to our custom object just by adding a single method.

>>> library[:3]
['Atomic Habits', 'Shoe Dog', 'Deep Work']

You would not want to reverse a library object, there is literally no meaning to it. But if you ever wanted to, addition of __getitem__ has the added benefit of the reversed method.

>>> [book for book in reversed(library)]
['Zero to One', 'Deep Work', 'Shoe Dog', 'Atomic Habits']

Conclusion

Effectively using Python data model allows us with minimum lines of code to inherit some of the most useful functionalities of Python. There are many more special methods in python giving us even more freedom to define our custom objects, that can take advantage of or work with standard methods that we already use on a daily bases.

Thank you!!!

--

--