teubi
teubi

Reputation: 13

Python: Why is the __len__() of a sequence ignored when iterating?

I'd like to create a custom sequence of elements. When iterating over it, the length of the sequence is ignored. Here is a minimal code example:

class Test():
    def __init__(self):
        pass

    def __len__(self):
        return 5

    def __getitem__(self, idx):
        return idx

t = Test()
for i, x in enumerate(t):
    print(i, x)

I'd expect to have only 0 0 to 4 4 printed out. In reality, the loop behaves as with an infinitely long sequence and goes on and on and on. len(t) returns 5 as expected.

What am I missing?

Upvotes: 1

Views: 360

Answers (2)

Gareth Latty
Gareth Latty

Reputation: 89087

Python expects __getitem__() to raise an IndexError when indexes outside of the possible range are requested, as noted in the docs:

Note for loops expect that an IndexError will be raised for illegal indexes to allow proper detection of the end of the sequence.

The advantage to this is that you don't have to implement __len__() which means your index-based iterable can be lazy, meaning unknown lengths or infinite lengths are possible.

This mirrors the way iterator objects and for loops work in Python - they continue to ask for values until an exception is reached (StopIteration for iterators), which makes the implementation easier - iter() just wraps with a loop incrementing the index until it hits the exception.

Upvotes: 4

drahnoel
drahnoel

Reputation: 227

You have to check yourself, if the key is out of bounds. __len__ is only used for two purposes. To be able to call len(obj) and it will be used, if no __bool__ method exists to determine, if the object evaluates to true in conditions.

Upvotes: 0

Related Questions