I want to create, in Python, a class behaving like a list but that could be iterated circularly use case example:
myc = SimpleCircle()
print(len(myc))
j = iter(myc)
for i in range (0, 5):
print(next(j))
it will print a b c d a
the code I tried so far is the one below
I know the issue is with my __next__
method which by the way seems ignored, I can use next even if I don't implement it
class SimpleCircle:
def __init__(self):
self._circle = ['a', 'b', 'c', 'd']
self._l = iter(self._circle)
def __len__(self):
return len(self._circle)
def __iter__(self):
return (elem for elem in self._circle)
def __next__(self):
try:
elem = next(self._l)
idx = self._circle.index(elem)
if idx < len(self._circle):
return elem
else:
return self._circle[0]
except StopIteration:
pass
This actually already exists with itertools.cycle
, for example:
from itertools import cycle
for x in cycle(['a', 'b', 'c', 'd']):
print(x)
will keep repeating the element.
Next you here mix up the iterable, and the iterator, those are frequently different things.
As an iterable we can keep iterating from self._circle
:
class SimpleCircle:
def __init__(self):
self._circle = ['a', 'b', 'c', 'd']
def __len__(self):
return len(self._circle)
def __iter__(self):
if not self._circle:
raise StopIteration
while True:
yield from self._circle
Or for an iterator:
class CycleIterator:
def __init__(self, iterable):
self.iterator = iter(iterable)
self.__next__ = self._iternext
self.idx = 0
self.list = []
def _iternext(self):
try:
x = next(self.iterator)
self.list.append(x)
return x
except StopIteration:
self.__next__ = self._iterlist
return self._iterlist()
def _iterlist(self):
try:
return self.list[self.index]
except IndexError:
raise StopIteration
finally:
self.index = (self.index + 1) % len(self.list)