By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,984 Members | 1,045 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,984 IT Pros & Developers. It's quick & easy.

for-else

P: n/a
So far in Python I've almost hated the 'else' of the 'for' loops:
- I have problems to remember its meaning;
- It gives me little problems when I later want to translate Python
code to other languages (and you always have to translate long-lived
code).
- I have used it only once, so far.

So so far I'd liked to see it removed from Python 3.0.

But then this article:
http://tratt.net/laurie/tech_article...guage_features
has shown me that my problems with the 'else' of the 'for' mostly come
from just its bad naming. The converge language is yet another very
Python-like language, and it uses a better naming (the word
"exhausted" is long and has a complex spelling for non-English
speakers, so it's not perfect):

for ...:
...
exhausted:
...
broken:
...

The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").

Bye,
bearophile
Mar 4 '08 #1
Share this Question
Share on Google+
23 Replies


P: n/a
On Tue, Mar 4, 2008 at 4:17 PM, Carl Banks <pa************@gmail.comwrote:
for ...:
...
exhausted:
...
broken:
...
>
The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").


I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.

For instance, if you have a (trivial) if...elif...else like this:

if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()
You forgot the break statement. The else suite will always be executed
in this loop. Kind of proves bearophiles point, for-else is really
tricky.
--
mvh Bjrn
Mar 4 '08 #2

P: n/a
For instance, if you have a (trivial) if...elif...else like this:
>
if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()
important "break" missing here...
else:
do_default_task()

or otherwise this code will do_task_i *and* do_default_task()...

-tkc
Mar 4 '08 #3

P: n/a
On Mar 4, 10:55 am, "BJrn Lindqvist" <bjou...@gmail.comwrote:
On Tue, Mar 4, 2008 at 4:17 PM, Carl Banks <pavlovevide...@gmail.comwrote:
for ...:
...
exhausted:
...
broken:
...
The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").
I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.
For instance, if you have a (trivial) if...elif...else like this:
if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()
You could roll it up into a for...else statement like this:
for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()

You forgot the break statement. The else suite will always be executed
in this loop. Kind of proves bearophiles point, for-else is really
tricky.
Ah ha, but that would have been a mistake with or without the else
clause....
Carl Banks
Mar 4 '08 #4

P: n/a
if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()
The if-elif-else structure that calls functions (like that above) can be
avoided with the code below:
def foo0(): print 'foo0'
def bar0(): print 'bar0'
def foo1(): print 'foo1'
def bar1(): print 'bar1'
def do_default_task(): print 'do_default_task'

do_task = { 0:foo0, 1:foo1, 2:bar0, 3:bar1, }

a = 1

# example of normal usage
if a in do_task.keys(): do_task[a]()
else: do_default_task()

# example of testing all functions in the dict as well as the default
function
for a in do_task.keys() + [8]: # 8 is a non-existent key in the do_task
dict
print "a is ",a,"and it gives this output:",
if a in do_task.keys(): do_task[a]()
else: do_default_task()

Carl Banks wrote:
On Mar 4, 10:55 am, "BJrn Lindqvist" <bjou...@gmail.comwrote:
>On Tue, Mar 4, 2008 at 4:17 PM, Carl Banks <pavlovevide...@gmail.comwrote:
>> for ...:
...
exhausted:
...
broken:
...

The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").

I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.

For instance, if you have a (trivial) if...elif...else like this:

if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()
You forgot the break statement. The else suite will always be executed
in this loop. Kind of proves bearophiles point, for-else is really
tricky.

Ah ha, but that would have been a mistake with or without the else
clause....
Carl Banks
This approach works well for me:

def foo0(): print 'foo0'
def bar0(): print 'bar0'
def foo1(): print 'foo1'
def bar1(): print 'bar1'

def do_default_task(): print 'do_default_task'

do_task = { 0:foo0, 1:foo1, 2:bar0, 3:bar1, }

a = 1

# example of normal usage
if a in do_task.keys(): do_task[a]()
else: do_default_task()
# example of testing
for i in range(len(do_task.keys)):
if a in do_task.keys(): do_task[a]()
else: do_default_task()

--
Shane Geiger
IT Director
National Council on Economic Education
sg*****@ncee.net | 402-438-8958 | http://www.ncee.net

Leading the Campaign for Economic and Financial Literacy

Mar 4 '08 #5

P: n/a
Carl Banks wrote:
there is a
rationale behind the name "else". *If you consider a for loop to be a
rolled-up if...elif...else statement
This is an interesting angle. I've always considered "for/else" to be
unintuitive, not because of "else", but because of the coupling with "for".
Instead, I think of this as a "break/else" statement, and everything gels
for me. The same applies to my comprehension of "while/else".

I wonder how this came to be called "for/else" or "for-else". I haven't
spotted that expression in the python docs yet.

With whatever name, I find the construct quite elegant and regularly useful.
Jeffrey
Mar 4 '08 #6

P: n/a
[BearOphile]
So far in Python I've almost hated the 'else' of the 'for' loops
FWIW, I'm very happy with for-else. Most of the time, you don't need
it, but when you do, it beats the heck out of doing silly tricks with
flags.

The primary use case is searching a container:

prep_tasks()
for item in container:
if predicate(item):
found_tasks()
break
else:
not_found_tasks()
follow_up_tasks

Raymond
Mar 4 '08 #7

P: n/a
Raymond HettInger:
FWIW, I'm very happy with for-else. Most of the time, you don't need
it, but when you do, it beats the heck out of doing silly tricks with
flags.
I'd like it to be renamed to something more natural :-)

Bye,
bearophile
Mar 4 '08 #8

P: n/a

<be************@lycos.comwrote in message
news:c4**********************************@u69g2000 hse.googlegroups.com...
| So far in Python I've almost hated the 'else' of the 'for' loops:
| - I have problems to remember its meaning;

Consider the following pseudoPython which you should understand:

label: loop
if cond:
do_something()
goto loop
else:
do_else()

We actually write the above as

while cond:
do_something()
else:
do_else()

Same meaning; do_else is executed when condition is false.

A for-loop is equivalent to a while loop with the condition 'iterator is
not exhausted'. So do_else when that condition is false -- the iterator is
exhausted.

Terry Jan Reedy

Mar 4 '08 #9

P: n/a
be************@lycos.com writes:
But then this article:
http://tratt.net/laurie/tech_article...guage_features
has shown me that my problems with the 'else' of the 'for' mostly
come from just its bad naming. The converge language is yet another
very Python-like language, and it uses a better naming (the word
"exhausted" is long and has a complex spelling for non-English
speakers, so it's not perfect):

for ...:
...
exhausted:
...
broken:
...
Rather than adding new keywords, I think the above would be better
spelled using the exception keywords::

for foo in bar_sequence:
# normal iteration
spam(foo)
if funky(foo):
break
except StopIteration, exc:
# the iterator stopped normally
eggs(exc)
else:
# the iterator exited abnormally, i.e. 'break'
sausage()
finally:
# always executed, even on 'break'
beans()

--
\ “[T]he question of whether machines can think [...] is about as |
`\ relevant as the question of whether submarines can swim.” |
_o__) —Edsger W. Dijkstra |
Ben Finney
Mar 5 '08 #10

P: n/a
That you could do yourself, CMIIW correct me if I'm wrong.

try:
* * for foo in iterex( bar_sequence ):
* * * * # normal iteration
* * * * spam(foo)
* * * * if funky(foo):
* * * * * * break
* * except StopIterationEx, exc:
* * * * # the iterator stopped normally
* * * * eggs(exc)
* * else:
* * * * # the iterator exited abnormally, i.e. 'break'
* * * * sausage()
* * finally:
* * * * # always executed, even on 'break'
* * * * beans()
Qualm, except would be needed in every for-, and in one sense doesn't
obey the semantics of exceptions. The end of a finite list is not an
exceptional element-- it's not one. However generator semantics don't
yield finite sequences-- and finity is an exception, but for-loops
don't use infinite ones.

Makes it sound (*subj've) like I'm hacking by using for-loops..... or,
like sequencetype.__iter__ is malformed. <staggered diminished chord>
Mar 5 '08 #11

P: n/a
>
The primary use case is searching a container:

prep_tasks()
for item in container:
if predicate(item):
found_tasks()
break
else:
not_found_tasks()
follow_up_tasks
I've found myself mimicing this again and again in c, and was pleased to
find it in python and use it regularely.
int i
for (i = 0 ; i < 10 ; ++i)
blah
if i == 10
not_found_tasks()

The discussion of words is silly. My surprise about "else following a for
loop.... what the heck ...." lasted excactly as long as it takes to read
this sentence.
tpt
Mar 5 '08 #12

P: n/a
On Mar 5, 10:44 pm, "Troels Thomsen" <nej tak ...wrote:
The primary use case is searching a container:
prep_tasks()
for item in container:
if predicate(item):
found_tasks()
break
else:
not_found_tasks()
follow_up_tasks

I've found myself mimicing this again and again in c, and was pleased to
find it in python and use it regularely.
int i
for (i = 0 ; i < 10 ; ++i)
blah
if i == 10
not_found_tasks()

The discussion of words is silly. My surprise about "else following a for
loop.... what the heck ...." lasted excactly as long as it takes to read
this sentence.

tpt
Mar 6 '08 #13

P: n/a
The idea of the if-else is:
.. depending on some condition either do this or do something else,
.. don't do them both.

If you approach a loop-else with this same idea, you get:
.. depending on the loop conditions either do the loop,
.. or do something else, but not both.

However the loop-else really works more like this:
.. try to do the loop;
.. if it starts but is interrupted by a break,
.. then do something else as well.

So they are completely different beasts, and if you try to use
or explain the one according to the rules of the other one,
you put a serious strain on your synapses.

The explanation that the if-else and the loop-else
follow the same pattern, runs more or less like this:
.. all conditions to run the loop to its completion were met,
.. which means that the loop-condition is not met (any more),
.. which means that we must do something else.
For me that is orwellian logic: success is failure.

The loop-else mechanism is not an easy one:
earlier in this thread Carl Banks turned it into an either/or construct
by omitting the crucial break; Jeffrey Barish initially found
the loop-else surprising; Sion Arrowsmith found it comprehensible
only after some hard thinking (as in the last paragraph).

The culprit is of course the keyword, else.
The idea of an easy follow-up after a loop is a good one.
But we need other keywords, I think at least three of them:
.. skipped - the loop did not run at all
.. broken - the loop was interrupted (by a break)
.. exhausted - the loop ran to completion.
The last two as suggested by Laurence Tratt in his Convergence.
e
--
Egbert Bouwman - Keizersgracht 197 II - 1016 DS Amsterdam - 020 6257991
================================================== ======================
Mar 8 '08 #14

P: n/a
The idea of the if-else is:
. *depending on some condition either do this or do something else,
. *don't do them both.
yes = the loop completed.

'else' isn't rechecking a piece of the loop, it's checking the loop.
Does it test successfully--- not the loop condition, the loop? What
is 'if a loop'? Make sense, I hold it does. (Holding is good--
something I can drop and give to someone else.) If 'else' gets hit, I
didn't do the loop.
Mar 8 '08 #15

P: n/a

"egbert" <eg************@hccnet.nlwrote in message
news:20********************@hccnet.nl...
| However the loop-else really works more like this:
| . try to do the loop;
| . if it starts but is interrupted by a break,
| . then do something else as well.

This is NOT how loop-else works for Python.
If you want to do something special on breaks,
put the break-only code before the break.

while loop_condition:
<loop statements>
if break_condition:
<break-only statements>
break
<more loop stuff>

| So they are completely different beasts, and if you try to use
| or explain the one according to the rules of the other one,
| you put a serious strain on your synapses.

I did not mean to broke your brain.

| The explanation that the if-else and the loop-else
| follow the same pattern, runs more or less like this:
| . all conditions to run the loop to its completion were met,
| . which means that the loop-condition is not met (any more),
| . which means that we must do something else.
| For me that is orwellian logic: success is failure.

I gave a clear and coherent explanation of how while derives from if,
and correspondingly, how while-else derives from if-else, to help those who
want to read and write Python code. Building on the pseudo-snippet above,
one can write

while loop_condition:
<loop statements>
if break_condition:
<break-only statements>
break
<more loop stuff>
else:
<completion-only statements>

Python allows one to have both break-only and completion-only sections
together in one compound statement and *without* having to fiddle with a
special flag variable. I am sorry if you cannot appreciate such elegance
and can only spit on it as 'orwellian'.

If the sense of else were reversed, one would have to write the clumbsier

complete = True # though false at this point
while loop_condition:
<loop statements>
if break_condition:
complete = False
break
<more loop stuff>
else:
<break-only statements>
if complete:
<completion-only statements>

Terry Jan Reedy

Mar 8 '08 #16

P: n/a
On Mar 8, 5:15 pm, "Terry Reedy" <tjre...@udel.eduwrote:
I gave a clear and coherent explanation of how while derives from if,
and correspondingly, how while-else derives from if-else, to help those who
want to read and write Python code. Building on the pseudo-snippet above,
one can write

while loop_condition:
<loop statements>
if break_condition:
<break-only statements>
break
<more loop stuff>
else:
<completion-only statements>

Python allows one to have both break-only and completion-only sections
together in one compound statement and *without* having to fiddle with a
special flag variable. I am sorry if you cannot appreciate such elegance
and can only spit on it as 'orwellian'.

Just to play Devil's advocate, there is one draw drawback to "such
elegance": when there are multiple break statements. Which means
you'd have to duplicate the break-only condition, or refactor somehow
(which may or may not be suitable).

There have been a couple occasions where I felt the best solution was
to a temporary varible like so:

completed = False
while loop_condition:
<loop statements>
if break_condition:
break
<more loop stuff>
if some_other_break_condition:
break
<more loop stuff>
else:
completed = False
if not completed:
<break-only statements>

It felt icky but we're all still here so it couldn't have been that
bad.
Carl Banks
Mar 10 '08 #17

P: n/a
On Mar 8, 4:15*pm, "Terry Reedy" <tjre...@udel.eduwrote:
"egbert" <egbert.bouw...@hccnet.nlwrote in message

news:20********************@hccnet.nl...
| However the loop-else really works more like this:
| . *try to do the loop;
| . *if it starts but is interrupted by a break,
| . *then do something else as well.

This is NOT how loop-else works for Python.
If you want to do something special on breaks,
put the break-only code before the break.

while loop_condition:
* <loop statements>
* if break_condition:
* * <break-only statements>
* * break
* <more loop stuff>

| So they are completely different beasts, and if you try to use
| or explain the one according to the rules of the other one,
| you put a serious strain on your synapses.

I did not mean to broke your brain.

| The explanation that the if-else and the loop-else
| follow the same pattern, runs more or less like this:
| . *all conditions to run the loop to its completion were met,
| . *which means that the loop-condition is not met (any more),
| . *which means that we must do something else.
| For me that is orwellian logic: success is failure.

I gave a clear and coherent explanation of how while derives from if,
and correspondingly, how while-else derives from if-else, to help those who
want to read and write Python code. *Building on the pseudo-snippet above,
one can write

while loop_condition:
* <loop statements>
* if break_condition:
* * <break-only statements>
* * break
* <more loop stuff>
else:
* <completion-only statements>

Python allows one to have both break-only and completion-only sections
together in one compound statement and *without* having to fiddle with a
special flag variable. *I am sorry if you cannot appreciate such elegance
and can only spit on it as 'orwellian'.

If the sense of else were reversed, one would have to write the clumbsier

complete = True # though false at this point
while loop_condition:
* <loop statements>
* if break_condition:
* * complete = False
* * break
* <more loop stuff>
else:
* <break-only statements>
if complete:
* <completion-only statements>

Terry Jan Reedy
Terry, instead of using "complete = True" and setting it to false on
failure, why not set "loop_completed = False" and set it to True if
the break condition is met?
Mar 10 '08 #18

P: n/a
egbert wrote:
The idea of the if-else is:
. *depending on some condition either do this or do something else,
. *don't do them both.
Indubitably, this statement is correct for other programming languages. I
was initially surprised by loop-else when learning Python because I
approached these constructs from the perspective of other programming
languages I knew, as you are doing. Before rejecting the Python
constructs, I asked myself whether the application of a different idea
resulted in a consistent, sensible interpretation. The key is to ask not
whether the Python constructs fit a particular idea of if-else and
loop-else, but whether a reasonable idea exists within which the Python
constructs make sense. For me and others in this thread, it does.
Different keywords would, no doubt, result in constructs that fit other
ideas better, but personally I am content with the current solution.
--
Jeffrey Barish

Mar 10 '08 #19

P: n/a
The idea of the if-else is:
. depending on some condition either do this or do something else,
. don't do them both.

constructs, I asked myself whether the application of a different idea
resulted in a consistent, sensible interpretation.
They could just as easily have defined the else of a 'for' to mean 'if
the loop only completed not an even number of times', giving these
different outputs:

for a in [1,2,3,4,5]:
if a3:
break
else:
print( 'bogus' )

for b in [1,2,3,4,5]:
if b4:
break
else:
print( 'foo' )

Just as easily, yes. Just as well? Just as meaningfully? Just as
consistently, usefully, and sensibly?

Possibly. The meaning of 'if a loop' could be construed to mean a
parity test or a binary test (if it didn't even complete once, i.p. if
the sequence was empty). If I tell you to do a loop, and then asked
if you did it, what factors in to your answer?

The docs say, 'A break statement executed in the first suite
terminates the loop without executing the else clause's suite', for
both for- and while-.

Does for-and and while-and or for-ifso and while-ifso sound more to
your liking?
Mar 10 '08 #20

P: n/a

"rockingred" <wi***********@yahoo.cawrote in message
news:7a**********************************@59g2000h sb.googlegroups.com...
On Mar 8, 4:15 pm, "Terry Reedy" <tjre...@udel.eduwrote:
If the sense of else were reversed, one would have to write the clumbsier

complete = True # though false at this point
while loop_condition:
<loop statements>
if break_condition:
complete = False
break
<more loop stuff>
else:
<break-only statements>
if complete:
<completion-only statements>

Terry Jan Reedy
Terry, instead of using "complete = True" and setting it to false on
failure, why not set "loop_completed = False" and set it to True if
the break condition is met?
[OE not quoting properly]
=====================
Because completion is False when broken? Actually, I am not sure what you
mean without seeing the snippet rewritten. Certainly, one could set
'broken=False' at top (tho not true) and 'broken = True' before breaking
and test for 'not broken' at end, but that is not an improvement.

tjr

Mar 11 '08 #21

P: n/a

"Carl Banks" <pa************@gmail.comwrote in message
news:df**********************************@n75g2000 hsh.googlegroups.com...
| Just to play Devil's advocate, there is one draw drawback to "such
| elegance": when there are multiple break statements. Which means
| you'd have to duplicate the break-only condition, or refactor somehow
| (which may or may not be suitable).

Yes, I knowingly glided over the possibility of multiple break statements
with common break-only code and no completion-only code (other than
artifactual flag setting as in your code below ;-). I presume that that
triple condition is even rarer than simpler situations where one of those
conditions is false.

| There have been a couple occasions where I felt the best solution was
| to a temporary varible like so:

But I will accept your testimony that the set of such use cases is not
empty.

| completed = False
| while loop_condition:
| <loop statements>
| if break_condition:
| break
| <more loop stuff>
| if some_other_break_condition:
| break
| <more loop stuff>
| else:
| completed = False

I presume you meant True

| if not completed:
| <break-only statements>
|
| It felt icky but we're all still here so it couldn't have been that
| bad.

tjr


Mar 11 '08 #22

P: n/a
On Mar 4, 11:27 pm, bearophileH...@lycos.com wrote:
>
The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").
Consider a loop with the following form:

while 1:
if <while-cond>:
<0-to-many times code block>
else:
<0-to-1 times code block>
break

A break, return or exception in the 0-to-many times code block will
obviously skip over the 'else' part of that if statement - it will
only be executed if <while-condevaluates as a false value. The above
code is actually equivalent to a normal Python while-loop:

while <while-cond>:
<0-to-many times code block>
else:
<0-to-1 times code block>

For loops aren't quite so straightforward since the termination
condition is tied up with the StopIteration exception, but the clause
keeps the same name as the corresponding clause on the while loop.
Thinking of it as break-else (as someone else posted) probably isn't a
bad way to look at the situation.

Cheers,
Nick.
Mar 11 '08 #23

P: n/a
On Mar 11, 4:43*am, NickC <ncogh...@gmail.comwrote:
On Mar 4, 11:27 pm, bearophileH...@lycos.com wrote:
The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").

Consider a loop with the following form:

while 1:
* if <while-cond>:
* * <0-to-many times code block>
* else:
* * <0-to-1 times code block>
* * break

A break, return or exception in the 0-to-many times code block will
obviously skip over the 'else' part of that if statement - it will
only be executed if <while-condevaluates as a false value. The above
code is actually equivalent to a normal Python while-loop:

while <while-cond>:
* <0-to-many times code block>
else:
* <0-to-1 times code block>

For loops aren't quite so straightforward since the termination
condition is tied up with the StopIteration exception, but the clause
keeps the same name as the corresponding clause on the while loop.
Thinking of it as break-else (as someone else posted) probably isn't a
bad way to look at the situation.

Cheers,
Nick.
Here's what try: else: says. "when control flows off the end of the
try clause." So at least their usage is consistent, even if
pessimistic. They might be using, "2. in addition to the persons or
things mentioned or implied: Who else was there?" - dictionary.com.

But conflate with 'if-else', the if and else are mutually exclusive.
Maybe the if-else should be if-orelse. One's a misnomer.
Mar 12 '08 #24

This discussion thread is closed

Replies have been disabled for this discussion.