Reputation: 13
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
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 anIndexError
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
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