简体   繁体   中英

python __iter__ (iterator and generator)

i read a book about ADV topics in python the author was try to explain the generator

that was his example to explain:

class rev:
    def __init__(self,data):
        self.data = data 
        self.index  = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
def main():
    reve = rev('zix')
    for i in reve:
        print(i)
if __name__=='__main__':
    main()

the main idea of this code is to reverse a generators the output is :

>>> 
x
i
z
>>> 

the think i found hard to understand is this part:

    def __iter__(self):
        return self 

can someone explain to my

When you do for x in xs , xs has to be an iterable , which means you can get an iterator out of it by iter(xs) , which you can do when xs.__iter__() is implemented. An iterator is required to implement __next__() , so that the in operator can consume it one by one by calling next() on it.

Now, in your case

reve = rev("hello") # is an iterable, as there is rev.__iter__()
rev1 = iter(reve)   # is an iterator, this is what rev.__iter__() returns
rev2 = next(rev1)   # now go on calling next() till you get StopIteration

Type the above snippet in REPL. Run it a few times. You will get a feel for it.

The iterator protocol is comprised of two methods:

  • __iter__ , and
  • __next__

Also, a requirement is that __iter__ returns self -- so if you have an obj that is an iterator then

obj is iter(obj) is obj.__iter__()

is true.

This is a good thing because it allows us to say iter = iter(obj) and if obj was already an iterator we still have the same object.

Since your class provides an implementation for next() it returns self so the caller will call that when looping over your object. In contrast, if you only wanted to wrap a data structure that already provides an implementation of __iter__ and next (eg list), you could return self.data.__iter__() from your class. In that case the caller would call next() defined on that object when doing a loop or list comprehension let's say.

class rev:
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def next(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]


class rev2:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return self.data.__iter__()


def main():
    for i in rev('zix'):  # str doesn't provide implementation for __iter__
        print(i)
    for i in rev2(['z', 'i', 'x']):  # list does so no need to implement next
        print(i)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM