PEP on breaking outer loops with StopIteration

Kris Kowal kris.kowal at cixar.com
Mon Jun 9 21:07:30 EDT 2008


I had a thought that might be pepworthy.  Might we be able to break
outer loops using an iter-instance specific StopIteration type?

This is the desired, if not desirable, syntax::

    import string
    letters = iter(string.lowercase)
    for letter in letters:
        for number in range(10):
            print letter, number
            if letter == 'a' and number == 5:
                raise StopIteration()
            if letter == 'b' and number == 5:
                raise letters.StopIteration()

The first StopIteration would halt the inner loop.  The second
StopIteration would halt the outer loop.  The inner for-loop would
note that the letters.StopIteration instance is specifically targeted
at another iteration and raise it back up.

For this output::

    a 0
    a 1
    a 2
    a 3
    a 4
    a 5
    b 0
    b 1
    b 2
    b 3
    b 4
    b 5

This could be incrementally refined with the addition of an "as"
clause to "for" that would be bound after an iterable is implicitly
iter()ed::

    import string
    for letter in string.lowercase as letters:
        …
        raise letters.StopIteration()

I took the liberty to create a demo using a "for_in" decorator instead
of a "for" loop::

    former_iter = iter

    class iter(object):
        def __init__(self, values):
            if hasattr(values, 'next'):
                self.iter = values
            else:
                self.iter = former_iter(values)
            class Stop(StopIteration):
                pass
            if hasattr(values, 'StopIteration'):
                self.StopIteration = values.StopIteration
            else:
                self.StopIteration = Stop

        def next(self):
            try:
                return self.iter.next()
            except StopIteration, exception:
                raise self.StopIteration()

    def for_in(values):
        def decorate(function):
            iteration = iter(values)
            while True:
                try:
                    function(iteration.next())
                except iteration.StopIteration:
                    break
                except StopIteration, exception:
                    if type(exception) is StopIteration:
                        break
                    else:
                        raise
        return decorate

    import string
    letters = iter(string.lowercase)

    @for_in(letters)
    def _as(letter):
        @for_in(range(10))
        def _as(number):
            print letter, number
            if letter == 'a' and number == 5:
                raise StopIteration()
            if letter == 'b' and number == 5:
                raise letters.StopIteration()

I imagine that this would constitute a lot of overhead in
StopIteration type instances, but perhaps a C implementation would use
flyweight StopIteration types for immutable direct subtypes of the
builtin StopIteration.

Kris Kowal



More information about the Python-list mailing list