1

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
asked Jul 31, 2014 at 21:26

2 Answers 2

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).

answered Jul 31, 2014 at 21:44
Sign up to request clarification or add additional context in comments.

Comments

1

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
answered Jul 31, 2014 at 21:34

2 Comments

Thanks! But one of the issues is actually that self.items will be very large, and I want to avoid storing it in the instance unless the user plans to use it over and over again.
I see what you mean - edited, it will now discard self.items the first time __iter__ is called if it isn't _cached.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.