简体   繁体   中英

Function generators vs class generators in Python 3

Why do function generators and class generators behave differently? I mean, with class generators I can use generator as many times as I want, but with function generators, I can only use it once? Why so?

def f_counter(low,high):
    counter=low
    while counter<=high:
        yield counter
        counter+=1

class CCounter(object):
    def __init__(self, low, high):
        self.low = low
        self.high = high
    def __iter__(self):
       counter = self.low
       while self.high >= counter:
            yield counter
            counter += 1

f_gen=f_counter(5,10)
for i in f_gen:
    print(i,end=' ')

print('\n')

for j in f_gen:
    print(j,end=' ')  #no output

print('\n')

c_gen=CCounter(5,10)
for i in c_gen:
    print(i,end=' ') 

print('\n')

for j in c_gen:
    print(j,end=' ')

Calling the f_gen() function produces an iterator (specifically, a generator iterator ). Iterators can only ever be looped over once. Your class is not an iterator, it is instead an iterable , an object that can produce any number of iterators .

Your class produces a new generator iterator each time you use for , because for applies the iter() function on the object you pass in, which in turn calls object.__iter__() , which in your implementation returns a new generator iterator each time it is called.

In other words, you can make the class behave the same way by calling iter(instance) or instance.__iter__() before looping:

c_gen = CCounter(5,10)
c_gen_iterator = iter(c_gen)
for i in c_gen_iterator:
    # ...

You can also make the CCounter() into an iterator by returning self from __iter__ , and adding an object.__next__() method ( object.next() in Python 2):

class CCounter(object):
    def __init__(self, low, high):
        self.low = low
        self.high = high
    def __iter__(self):
       return self
    def __next__(self):
       result = self.low
       if result >= self.high:
           raise StopIteration()
       self.low += 1
       return result

Your class is an iterable, but not an iterator itself. Each time you call iter on it, you get a new iterator.

If you want to replicate the behavior of the generator function with a class, then you want an iterator like this:

class CCounter(object):
    def __init__(self, low, high):
        self.low = low
        self.high = high
        self.counter = self.low
    def __iter__(self):
       return self
    def __next__(self):
       if self.counter > self.high:
           raise StopIteration()
       val = self.counter
       self.counter += 1
       return val

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