itertools: cycle(it) is approximately chain(*repeat(it))

Beni Cherniavsky cben at techunix.technion.ac.il
Sun Mar 9 20:18:48 CET 2003


I've got this simple observation: `cycle` is just a repeated chaing of
an iterable to itself.  This could be easily expressed through `chain`
and `repeat`, if `chain` would recieve the list of iterables as a
single iterable argument (potentially infinite generator), instead of
recieving a variable list of arguments that's forced into a tuple by
Python.  Hence I propose to change the interface of `chain` in this
way.  The pure-Python equivallent implementation given in the docs
stays almost without change:

    def chain(iterables):      #  was chain(*iterables)
        for it in iterables:
            for element in it:
                yield element

Then one would be able to say ``chain(repeat([1, 2, 3]))`` as an
equivallent to ``cycle([1, 2, 3])`` but it's more general - any
iterable of iterables could be used as an argument to chain.  It
expresses a generic iterator "flattening" operation.

There is one more thing missing for this to work: `cycle`
automagically remembers the values of its iterable, so it works even
for destructive iterators.  This is something which should really be
exposed as a primitive in itertools, IMHO: something that would take
an iterator and return a non-destructive iterable that only reads the
iterator only as far as the longest iteration over it got to.  Here is
a minimal implementation:

class ilist(object):
    def __init__(self, iterable):
        self.source = iter(iterable)
        self.mem = []
    def __iter__(self):
        for i in itertools.count():
            while len(self.mem) <= i:
                self.mem.append(self.source.next())
            yield self.mem[i]

Arguebly, this could use implementation of all other list methods...

Now ``cycle(it)`` would become equivallent to
``chain(repeat(ilist(it)))``.

-- 
Beni Cherniavsky <cben at tx.technion.ac.il>






More information about the Python-list mailing list