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=' ')
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.
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).
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With