Making the len function work on your Python objects

Topic Series: Dunder methods

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
2 minute read Works on Python 3.7—3.10
Share
Python Morsels
Watch as video
01:54

In Python, you can make the built-in len function work on your objects.

The len function only works on objects that have a length

The built-in len function works on some objects, but not on others. Only things that have a length work with the len function.

Lists, sets, dictionaries, strings, and most data structures in Python have a length:

>>> numbers = [2, 1, 3, 4, 7, 11, 18]
>>> len(numbers)
7

But numbers don't:

>>> n = 10
>>> len(n)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

When making a class in Python, you can control whether instances of that class have a length.

Python's built-in len function calls the __len__ method (pronounced "dunder len") on the object you give it.

So if that object has a __len__ method, it has a length:

>>> numbers = [2, 1, 3, 4, 7, 11, 18]
>>> numbers.__len__()
7

If it doesn't have a __len__ method, the len function raises a TypeError instead:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

How to make instances of your class have a length?

Python's random module has a function (choice) which can randomly select an item from a given sequence.

>>> import random
>>> colors = ['red', 'blue', 'green', 'purple']
>>> random.choice(colors)
'purple'

This choice function only works on objects that can be indexed and have a length.

Here we have a class named ForgivingIndexer:

class ForgivingIndexer:

    def __init__(self, sequence):
        self.sequence = sequence

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

This class has a __init__ method and a __getitem__ method. That __getitem__ method allows instances of this class to be indexed using square brackets ([]).

But this isn't quite enough to allow our ForgivingIndexer objects to work with the random.choice function. If we pass a ForgivingIndexer object to the random.choice function, we'll get an error:

>>> import random
>>> fruits = ForgivingIndexer(['apple', 'lime', 'pear', 'watermelon'])
>>> random.choice(fruits)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.10/random.py", line 378, in choice
    return seq[self._randbelow(len(seq))]
TypeError: object of type 'ForgivingIndexer' has no len()

Python gives us an error because ForgivingIndexer objects don't have a length, which the random.choice function requires. These objects don't work with the built-in len function:

>>> fruits = ForgivingIndexer(['apple', 'lime', 'pear', 'watermelon'])
>>> len(fruits)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'ForgivingIndexer' has no len()

In order to support the built-in len function, we can add a __len__ method to this class:

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

Now instances of this class have a length:

>>> import random
>>> fruits = ForgivingIndexer(['apple', 'lime', 'pear', 'watermelon'])
>>> len(fruits)
4

And they also work with random.choice:

>>> random.choice(fruits)
'apple'

Summary

You can make your objects work with the built-in len function by adding a __len__ method to them. You'll pretty much only ever add a __len__ method if you're making a custom data structure, like a sequence or a mapping.

Topic Trail: Dunder methods

You can overload many operators, protocols, and bits of functionality on your Python objects by implementing dunder methods.

To track your progress on this Python Morsels topic trail, sign in or sign up.

0%
Write more Pythonic code

Need to fill-in gaps in your Python skills? I send regular emails designed to do just that.

Python Morsels
Watch as video
01:54