Python's iterators and generators


In [ ]:
# import python packages here...

Iterator

TODO...


In [ ]:
class Counter:
    
    def __init__(self, max_value):
        self.current_value = 0
        self.max_value = max_value
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current_value >= self.max_value:
            raise StopIteration

        self.current_value += 1
        
        return self.current_value

###
 
cpt = Counter(10)   # cpt is an iterator

for i in cpt:
    print(i)

###

cpt = Counter(10)   # cpt is an iterator
print(next(cpt))
print(next(cpt))

###

print([i for i in cpt])

Generator

Goal: increase memory efficiency of some functions. See https://wiki.python.org/moin/Generators.

Generator (or generator function)

https://docs.python.org/3/glossary.html#term-generator

A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn't clear, using the full terms avoids ambiguity.


In [ ]:
def counter(max_value):
    current_value = 0
    while current_value < max_value:
        yield current_value    # temporarily suspends processing, returning i and remembering the location execution state (including local variables and pending try-statements)
        current_value += 1

In [ ]:
for elem in altrange(10):
    print(elem)

In [ ]:
list(altrange(10))

In [ ]:
def gen(nmax):    # "gen" is a "generator"
    n = 0
    while n<nmax:
        yield n
        n += 1

gi = foo()        # "gi" is a "generator iterator"

for n in gi;
    print(n)

Generator iterator

https://docs.python.org/3/glossary.html#term-generator-iterator

An object created by a generator function.

Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation).


In [ ]:
altrange(10)

Generator expression

https://docs.python.org/3/glossary.html#term-generator-expression

An expression that returns an iterator. It looks like a normal expression followed by a for expression defining a loop variable, range, and an optional if expression. The combined expression generates values for an enclosing function:


In [ ]:
(i*i for i in range(10))

In [ ]:
list((i*i for i in range(10)))

In [ ]:
list(i*i for i in range(10))

In [ ]:
sum(i*i for i in range(10))         # sum of squares 0, 1, 4, ... 81

In [ ]:

Strange test: does it memorize the object state ? => that's a stupid question as the state of an object is always "memorized"... This only concerns function's internal state (i.e. yield only memorize the state of the function it is defined in, not the objects state)...


In [ ]:
class AltRange:
    def __init__(self, n):
        self.i = 0
        self.n = n
        
    def run(self):
        self.i = 0
        while self.i < self.n:
            yield self.i    # temporarily suspends processing, returning i and remembering the location execution state (including local variables and pending try-statements)
            self.i += 1

In [ ]:
obj = AltRange(10)
obj.run()

In [ ]:
list(obj.run())

In [ ]:
for elem in obj.run():
    obj.i += 1
    print(elem)