Early halt for iterating a_list and iter(a_list)

Lie Lie.1296 at gmail.com
Fri Aug 22 16:23:18 CEST 2008


On Aug 15, 9:55 pm, Fredrik Lundh <fred... at pythonware.com> wrote:
> Lie wrote:
> > When you've got a nested loop a StopIteration in the Inner Loop would
> > break the loop for the outer loop too:
>
> > a, b, c = [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]
>
> > def looper(a, b, c):
> >     for a_ in a:
> >         for b_ in b:
> >             for c_ in c:
> >                 print a_, b_, c_
>
> > looper(a, b, c)  # Intended behavior [1]
> > a, b, c = iter(a), b, iter(c)  # b is intentionally not iter()-ed
> > looper(a, b, c)  # Inner StopIteration prematurely halt outer loop [2]
>
> iterators are once-only objects.  there's nothing left in "c" when you
> enter the inner loop the second time, so nothing is printed.
>

Ah, now I see. You have to "restart" the iterator if you want to use
it the second time (is it possible to do that?).

>  >>> a = range(10)
>  >>> a = range(5)
>  >>> a = iter(a)
>  >>> for i in a:
> ...     print i
> ...
> 0
> 1
> 2
> 3
> 4
>  >>> for i in a:
> ...     print i
> ...
>  >>>
>
>  > This is a potential problem since it is possible that a function that
>  > takes an iterable and utilizes multi-level looping could be
>  > prematurely halted and possibly left in intermediate state just by
>  > passing an iterator.
>
> it's a problem only if you confuse iterators with sequences.

I see, but if/when a function expects a sequence but then fed with an
iterator, it would be against duck-typing to check whether something
is a sequence or an iterator, but iterator is good for one iteration
only while sequence is good for multiple usage. So is there a clean
way to handle this? (i.e. a design pattern that allows sequence and
iterator to be treated with the same code)

If there is no such design pattern for that problem, should one be
available? I'm thinking of one: "all iterables would have
iterable.restart() method, which is defined as 'restarting the
iterator for iterator' or 'do nothing for sequences'."

Then both sequence and list can be treated like this:
for a_ in a:
    b.restart()
    for b_ in b:
        c.restart()
        for c_ in c:
            print a_, b_, c_

This is useful if b, c is huge (so copying the list is out of the
question) but needs to be used multiple times in a nested loop. For
the general cases where the iterators is used only once,
iterable.restart() wouldn't need to be called at all and wouldn't even
need a code change (on the spirit of make the common things easy and
the rare thing possible).

Wait a minute... I've got an idea, we could use itertools.tee to copy
the iterator and iterating on the copy, like this right?:

for a_ in a:
    b, b_copy = itertools.tee(b)
    for b_ in b_copy:
        c, c_copy = itertools.tee(c)
        for c_ in c_copy:
            print a_, b_, c_

That works with both "requirement": able to handle sequence and
iterator with the same code and the code for common cases where
iterators are used once only wouldn't need to be changed.
Personally though, I don't think it's a clean solution, looks a bit of
hackery.

> </F>



More information about the Python-list mailing list