Subscribe to andrewsw's Blog        RSS Feed
-----

Python Iterator and Generator

Icon 1 Comments
This isn't intended as a tutorial, just examples of an iterator and generator posted for ease of reference. Hopefully, the examples are clear enough that they will inspire you (and me) to investigate further.

They were originally posted in this thread. The task is to be able to obtain a sequence of numbers 0,1,2, etc., but to be able to supply a separate set of values to exclude from the output.

I am not suggesting that an iterator or generator are needed for this task. Read the thread where other, simpler, alternatives are discussed, for example print({x for x in range(0, 19)} - {15, 16}) which uses a set comprehension.

Roughly, an iterator is more explicit, a generator is more convenient. An iterator would be used if we wanted to extend a Python object to be iterable, otherwise use a simple generator.

Iterator:
class RangeEx(object):
    def __init__(self, n, excludes):
        self.i = 0
        self.n = n
        self.excludes = excludes

    def __iter__(self):
        return self

    def __next__(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            if self.i in self.excludes:
                self.__next__()
            return i
        else:
            raise StopIteration()

# usage:
print(list(RangeEx(19, (15, 16)))) # 15 and 16 are the values to exclude.

# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18]


Note that in Python 2 __next__() is called next(). Assigning next = __next__ can, I believe, be used for compatibility.

The essential methods are __iter__ and __next__. __iter__ returns the iterator object which, in this case, is the object itself. It needn't be with a more detailed example (extending an object). The __iter__ object is used in for and in statements.

__next__ returns the next value in the sequence, or raises StopIteration() when the sequence ends. Notice that, when one of the excluded values is encountered, we call __next__ again, so a value hasn't yet been returned.

Generator:
def RangeEx(n, excludes):
    i = 0
    while i < n:
        while i in excludes:
            i += 1
        if i < n:
            yield i
        i += 1

print(list(RangeEx(19, (15, 16))))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18]

Basically, the generator obtains a sequence of values, but pauses to output, to yield, each individual value. Notice that there is no need to raise StopIteration(), these details are hidden behind 'yield' and the sequence will finish when there are no more values to return.

Iterators, generators and decorators

1 Comments On This Entry

Page 1 of 1

jon.kiparsky 

26 January 2016 - 04:42 PM

Quote

Basically, the generator obtains a sequence of values, but pauses to output, to yield, each individual value.


It would be more accurate to say that a generator calculates a value when it's needed, so the sequence of values never exists all at once. This means that a RangeEx(1000000000, (3, 4)) takes exactly the same space as a RangeEx(100, (3, 4)), which is a great savings if n gets large.
0
Page 1 of 1

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

December 2019

S M T W T F S
1234567
891011 12 1314
15161718192021
22232425262728
293031    

Tags

    Recent Entries

    Recent Comments

    Search My Blog

    1 user(s) viewing

    1 Guests
    0 member(s)
    0 anonymous member(s)

    Categories