I want to have a class with an __iter__ method that depends on a cache argument when instances are created.
If an instance is created with cache=False, then the instance should be iterated through like a generator (creating and discarding each item as needed). If an instance is created with cache=True, then the object should create a list of the items, store it, and then have it available to be iterated through repeatedly.
So for example (this doesn't work):
class Foo(object):
def __init__(self, max=5, cache=False):
if cache:
self.items = range(max)
self.__iter__ = iter(self.items)
else:
self.__iter__ = iter(range(max))
Then if I did this, the expected result is to print 0--4, and to have this available only once.
test_obj = Foo()
for i in test_obj:
print i, # 0 1 2 3 4
for i in test_obj:
print i, # empty
But if I do this, the expected result is to print 0--4, and to have this available as many times as necessary.
test_obj = Foo(cache=True)
for i in test_obj:
print i, # 0 1 2 3 4
for i in test_obj:
print i, # 0 1 2 3 4
2 Answers 2
Python won't look up a __op__ method on an instance, only on a class. So your idea of defining __iter__ from __init__ won't work. However, you can implement __iter__ such that its behavior differs dending on a value set in __init__. Here's one way:
class Foo(object):
def __init__(self, max=5, cache=False):
if cache:
self.iterable = range(max)
else:
self.iterable = iter(xrange(max))
def __iter__(self):
return iter(self.iterable)
This works because an iterator is "iterable" just like a sequence is. However, its __iter__ method returns itself (so no matter how many times you can iter on it, you can only iterate through its values once).
Comments
Here is one way to approach it - if it's not cached, discard self.items the first time __iter__ is called:
class Foo(object):
def __init__(self, max_=5, cached=False):
self.items = range(max_)
self._cached = cache
def __iter__(self):
out = self.items
if not self._cached:
self.items = []
return iter(out)
In action:
>>> test_obj = Foo()
>>> for i in test_obj:
print i,
0 1 2 3 4
>>> for i in test_obj:
print i,
>>> test_obj = Foo(cached=True)
>>> for i in test_obj:
print i,
0 1 2 3 4
>>> for i in test_obj:
print i,
0 1 2 3 4
2 Comments
self.items the first time __iter__ is called if it isn't _cached.