user2034412
user2034412

Reputation: 4282

Define method in init

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

Upvotes: 1

Views: 142

Answers (2)

Blckknght
Blckknght

Reputation: 104832

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

Upvotes: 2

jonrsharpe
jonrsharpe

Reputation: 122154

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

Upvotes: 1

Related Questions