I am writing a class that defines __iter__
and __len__
, where the value of __len__
depends on the iterator returned by __iter__
. I am getting an interesting RecursionError
.
Language versions: Python 3.8.6, 3.7.6. Examples are for illustrating the error only.
In the following example, Iter.__len__()
attempts to unpack self
, store the result in a list
, and then attempts to call the built-in list.__len__()
on that list to get the length.
>>> class Iter:
... def __iter__(self):
... return range(5).__iter__()
... def __len__(self):
... return list.__len__([*self])
...
>>> len(Iter())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __len__
File "<stdin>", line 5, in __len__
File "<stdin>", line 5, in __len__
[Previous line repeated 993 more times]
File "<stdin>", line 3, in __iter__
RecursionError: maximum recursion depth exceeded in comparison
However, if I define the class Iter
as the following, where Iter.__len__()
explicitly unpacks the iterator as returned by Iter.__iter__()
:
>>> class Iter:
... def __iter__(self):
... return range(5).__iter__()
... def __len__(self):
... return list.__len__([*self.__iter__()])
...
>>> len(Iter())
5
Then there is no error.
From the traceback, it seems that list.__len__()
is trying to call Iter.__len__()
, even thought the argument provided is supposedly already a native list
object. What is the reason for the RecursionError
?
According to schwobaseggl, using set
instead of list
will not cause a RecursionError
:
>>> class Iter:
... def __iter__(self):
... return range(5).__iter__()
... def __len__(self):
... return set.__len__({*self})
...
>>> len(Iter())
5