Sign in to your Python Morsels account to save your screencast settings.
Don't have an account yet? Sign up here.
In Python, you can make the built-in len
function work on your objects.
len
function only works on objects that have a lengthPython's 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()
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'
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.
Need to fill-in gaps in your Python skills?
Sign up for my Python newsletter where I share one of my favorite Python tips every week.
Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.