Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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=' ')
like image 571
chanzerre Avatar asked Aug 10 '16 11:08

chanzerre


People also ask

What is difference between generator and function in Python?

Generator Functions are memory efficient, as they save a lot of memory while using generators. A normal function will return a sequence of items, but before giving the result, it creates a sequence in memory and then gives us the result, whereas the generator function produces one output at a time.

What is function generator in Python?

Python generators are a simple way of creating iterators. All the work we mentioned above are automatically handled by generators in Python. Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).

What is the difference between generator and iterator in Python?

Iterators are the objects that use the next() method to get the next value of the sequence. A generator is a function that produces or yields a sequence of values using a yield statement. Classes are used to Implement the iterators. Functions are used to implement the generator.

When should I use Python generators?

Generators are great when you encounter problems that require you to read from a large dataset. Reading from a large dataset indirectly means our computer or server would have to allocate memory for it. The only condition to remember is that a Generator can only be iterated once.


2 Answers

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
like image 55
Martijn Pieters Avatar answered Oct 11 '22 23:10

Martijn Pieters


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
like image 31
Blckknght Avatar answered Oct 11 '22 21:10

Blckknght