itertools.cycle
is cool, but it has an internal "storage" that uses up memory. And I wanted to practice "tampering" with built-in methods of classes, which is a new field for me. I tried to implement a class which mostly behaves like a list, but when we try to iterate over it (with for
loop, for example), it would jump back to the beginning when it reaches the end, therefore cycling indefinitely. Here's what I've come up with:
class InfiniteListEmptyError(Exception):
pass
class InfiniteList(list):
"""
Pretty much a regular list, but when iterated over, it does not stop in the end.
Instead, it iterates indefinitely.
"""
def __init__(self, arg):
super(InfiniteList, self).__init__(arg)
def __getattr__(self, name):
if name == "it":
# get iterator
result = object.__getattribute__(self, name)
else:
try:
result = super(InfiniteList, self).__getattribute__(name)
except AttributeError:
try:
result = self.it.__getattribute__(name)
except AttributeError:
# Initialize iterator cuz it's not initialized
self.__iter__()
result = self.it.__getattribute__(name)
return result
def __iter__(self):
it = super(InfiniteList, self).__iter__()
self.it = it
return self
def __next__(self):
try:
result = next(self.it)
except StopIteration:
self.__iter__()
try:
result = next(self.it)
except StopIteration:
raise InfiniteListEmptyError("Could not iterate. List is empty!")
return result
# TESTS
a = InfiniteList(tuple()) #empty list
print(a)
print(a.__length_hint__()) #testing iterator attributes
print(a.__eq__([1,3,5])) # false
# should raise exception, since the list is empty
try:
for i in a:
print(i)
except InfiniteListEmptyError:
print("List is empty!")
a.append(1)
a.extend([3,5])
print(a)
print(a.__eq__([1,3,5])) #true
# infinite loop
for i in a:
print(i)
It works fine, it does not store an auxiliary list, so it does not consume twice as much memory, like itertools.cycle
. But I'm not sure about the possible caveats when it comes to handling attributes. And yeah, I had to implement iterator within the InfiniteList
class itself, so I had to redirect the calls to iterator's methods (like __length_hint__
) to the saved iterator self.it
.
a = list(range(50)); b = InfiniteList(a)
you are effectively copying your initial list, right? So what is the exact advantage overitertools.cycle
? – Mathias Ettinger Oct 13 '16 at 18:24