PEP on breaking outer loops with StopIteration

K

Kris Kowal

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
 
P

Paul Hankin

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()

Have you checked out http://www.python.org/dev/peps/pep-3136/

It contains exactly this idea, but using 'break letters' rather than
'raise letters.StopIteration()'. I think I like the PEP's syntax
better than yours, but anyway, it was rejected.
 
K

Kris Kowal

Have you checked out http://www.python.org/dev/peps/pep-3136/

It contains exactly this idea, but using 'break letters' rather than
'raise letters.StopIteration()'. I think I like the PEP's syntax
better than yours, but anyway, it was rejected.

I concur that "break letters" is better than "raise
letters.StopIteration()". Perhaps the novelty of the implementation
idea (adding another exception case to the "while: try" that
must already be there, and the specialized exception type) can wake this
dead issue. Maybe "break letters" could under the hood raise the
specialized StopIteration.

But, then again. Guido has said, "No", already on other, albeit
subjective, grounds.
I'll drop it or champion it if there's interest.

Kris Kowal
 
D

Dan Bishop

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()

You can break out of outer loops now with the proper (ab)use of
exceptions:


class BreakOuter(Exception):
pass

try:
for letter in string.lowercase:
for number in xrange(10):
print letter, number
if letter == 'a' and number == 5:
break
if letter == 'b' and number == 5:
raise BreakOuter()
except BreakOuter:
pass


Or, for consistency:

class BreakInner(Exception):
pass

class BreakOuter(Exception):
pass

try:
for letter in string.lowercase:
try:
for number in xrange(10):
print letter, number
if letter == 'a' and number == 5:
raise BreakInner()
if letter == 'b' and number == 5:
raise BreakOuter()
except BreakInner:
pass
except BreakOuter:
pass
 
D

Diez B. Roggisch

Kris said:
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 the record: I share GvR's opinion on the general usefulness.

Additionally, your syntax is impossible. There isn't always a containing
variable for the iterable.

And if you meant "letter", it's ambigous. It is perfectly legal in
python to do this:

class Foo(object):
StopIteration = StopIteration

for letter in [Foo(), Foo()]:
for number in range(10):
raise letter.StopIteration


Diez
 
N

nwinters3000

You can break out of outer loops now with the proper (ab)use of
exceptions:

class BreakInner(Exception):
pass
class BreakOuter(Exception):
pass
try:
for letter in string.lowercase:
try:
for number in xrange(10):
print letter, number
if letter == 'a' and number == 5:
raise BreakInner()
if letter == 'b' and number == 5:
raise BreakOuter()
except BreakInner:
pass
except BreakOuter:
pass

I prefer having a new function wrapping the inner loop and using both
break and return, but this doesn't allow you to "break 1 loop up and 2
loops up", and doesn't help to continue a particular loop further up

def innerLoop(letter):
for number in xrange(10):
print letter, number
if letter == 'a' and number == 5:
break
if letter == 'b' and number == 5:
return

for letter in string.lowercase:
innerLoop(letter)


In response to the suggested syntax, I have found occasions where I
iterate through the same variable [say searching for duplicates within
some tolerance to merge into one item] in an inner loop. I also don't
see how it would extend to something like:

for evenNumber in [x*2+1 for x in xrange(5)]: print evenNumber
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top