Search code examples
pythoniteratorgenerator

Why does a yield from inside __next__() return generator object?


I am using yield to return the next value in the __next__() function in my class. However it does not return the next value, it returns the generator object.

I am trying to better understand iterators and yield. I might be doing it in the wrong way.

Have a look.

class MyString:
    def __init__(self,s):
        self.s=s

    def __iter__(self):
        return self

    def __next__(self):
        for i in range(len(self.s)):
            yield(self.s[i])

r=MyString("abc")
i=iter(r)
print(next(i))

This returns:

generator object __next__ at 0x032C05A0


Solution

  • next pretty much just calls __next__() in this case. Calling __next__ on your object will start the generator and return it (no magic is done at this point).


    In this case, you might be able to get away with not defining __next__ at all:

    class MyString:
        def __init__(self,s):
            self.s=s
    
        def __iter__(self):
            for i in range(len(self.s)):
                yield(self.s[i])
            # Or...
            # for item in self.s:
            #     yield item
    

    If you wanted to use __iter__ and __next__ (to define an iterator rather than simply making an iterable), you'd probably want to do something like this:

    class MyString:
        def __init__(self,s):
            self.s = s
            self._ix = None
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self._ix is None:
                self._ix = 0
    
            try:
                item = self.s[self._ix]
            except IndexError:
                # Possibly reset `self._ix`?
                raise StopIteration
            self._ix += 1
            return item