Question

Kind of a weird question about iterators. While investigating a different question, I found the following. Here is an iterable that works:

class CacheGen(object):
    def __init__(self, iterable):
        if isinstance(iterable, (list, tuple, dict)):
            self._myiter = iterable
        else:
            self._myiter = list(iterable)
    def __iter__(self):
        return self._myiter.__iter__()
    def __contains__(self, key):
        return self._myiter.__contains__(key)
    def __getitem__(self, key):
        return self._myiter.__getitem__(key)

Here is a similar iterable that doesn't:

class CacheGen2(object):
    def __init__(self, iterable):
        if isinstance(iterable, (list, tuple, dict)):
            self._myiter = iterable
        else:
            self._myiter = list(iterable)
        self.__iter__ = self._myiter.__iter__
        self.__contains__ = self._myiter.__contains__
        self.__getitem__ = self._myiter.__getitem__

Note that they are really doing about the same thing, but one delegates, and the other just assigns my class constructor to the list's. Any ideas why? Note that it has an iter function in the class, I can call it directly and get a valid iterator, but the 'normal' functions don't work.

xr = xrange(100)
cg = CacheGen(xr)
list(cg)
[0,1,2,3...

cg2 = CacheGen2(xr)
list(cg2)
TypeError                                 Traceback (most recent call last)
<ipython-input-83-1f4da2c55acb> in <module>()
----> 1 list(cg2)

TypeError: 'CacheGen2' object is not iterable

cg2.__iter__
<method-wrapper '__iter__' of list object at 0x0000000006695C08>

cg2.__iter__()
<listiterator at 0x669c438>

iter(cg2)
TypeError                                 Traceback (most recent call last)
<ipython-input-86-b62853ce1dab> in <module>()
----> 1 iter(cg2)

TypeError: 'CacheGen2' object is not iterable
Was it helpful?

Solution

Magic methods like __iter__ are looked up on the class, not the instance, so it doesn't work to assign them on self. They have to actually exist on the class.

OTHER TIPS

The correct way of doing what you want is:

class CacheGen(object):

    def __init__(self, iterable):
        if isinstance(iterable, (list, tuple, dict)):
            self._myiter = iterable
        else:
            self._myiter = list(iterable)

    def __iter__(self):
        for value in self._myiter:
            yield value
    ...

If you would like to create your own Iterator class,

your class must override the following methods

def __iter__(self)

and

def next(self)

then only your class will be considered as an iterator

the simple example of iterator is,

class MyIter(object):
    def __init__(self, val):
        self.val = val

    def __iter__(self):
        return self

    def next(self):
        if self.val >= 5:
            raise StopIteration
        self.val += 1
        return self.val

print list(MyIter(0))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top