[Python-ideas] Is this PEP-able? for X in ListY while conditionZ:

Steven D'Aprano steve at pearwood.info
Sat Jun 29 17:56:32 CEST 2013


On 29/06/13 20:09, Nick Coghlan wrote:
> On 28 June 2013 18:20, Wolfgang Maier
> <wolfgang.maier at biologie.uni-freiburg.de> wrote:

>> for x in iterable:
>>      breakif x<0 # itself translating to if x<0: break
>>      yield x
[...]
> This (or, more accurately, a slight variant that doesn't need a new
> keyword) actually sounds quite attractive to me. My rationale for that
> ties into the just rejected PEP 315, which tried to find an improved
> "loop and a half" syntax for Python, as well as the ongoing confusion
> regarding the meaning of the "else" clause on while and for loops.
[...]
> Rather than adding a new keyword, we could simply expand the syntax
> for the existing break statement to be this:
>
>      break [if <EXPR>]
>
> This would simplify the above two standard idioms to the following:
>
>      while True:
>          # Iteration setup
>          break if termination_condition
>          # Remainder of iteration

It doesn't really *simplify* the standard idiom though. It just saves a line and, trivially, one indent:

while True:
     if condition:
         break
     ...

And it doesn't even save that if you write it like this:

while True:
     if condition: break
     ...


In fact, I would argue the opposite, it's not *simpler* it is *more complex* because it is a special case for the if keyword:

break if condition  # allowed
continue if condition  # maybe allowed?
return 'spam' if condition  # probably disallowed
pass if condition  # what's the point?
raise Exception if condition  # probably disallowed
x += 1 if condition  # almost certainly disallowed

to say nothing of:

break if condition else continue

Given this suggestion, now people have to learn a third case for "if":

- "if condition" starts a new block and must be followed by a colon;

- unless it follows an expression, in which case it is the ternary operator and must be followed by "else expression";

- unless it follows a break (or continue? return? raise? ...) in which case it must not be followed by "else".


>      for x in data:
>          break if desired_value(x)
>      else:
>          raise ValueError("Value not found in {:100!r}".format(data))
>
> A "bare" break would then be equivalent to "break if True". The "else"
> clause on the loop could then be *explicitly* documented as associated
> with the "break if <X>" form - the else only executes if the break
> clause is never true. (That also becomes the justification for only
> allowing this for break, and not for continue or return: those have no
> corresponding "else" clause)

It is certainly not correct that the else of while...else and for...else is necessarily associated with the (implied) "if" of "break if condition". It's currently legal to write a for...else loop with no break. Pointless, but legal, so it must remain legal, and you still have an else without an if. Likewise we can do things like this:


for x in data:
     break
else:
     print("data must be empty")

for x in data:
     if condition:
         return x
else:
     print("condition was never true")


I think that it is simply the case that for...else and while...else were misnamed. Great concept, but the keyword doesn't make sense. It isn't an *else* clause in the ordinary English sense of the word, but a *then* clause, and should have been spelled "for...then". But we're stuck with it until Python 4000. Trying to retcon the "else" as being associated with an implied "if" is simply misguided.



> Once the break statement has been redefined this way, it *then*
> becomes reasonable to allow the following in comprehensions:
>
>      data = [x for x in iterable break if x is None]


Which reads wrongly for something which is intended to be a single expression. Current comprehensions read like an English expression:

[this for x in iterable]
"Do this for each x in iterable."

[this for x in iterable if condition]
"Do this for each x in iterable if condition is true."

are both reasonable English-like expressions. Even the proposed (and rejected) form:

[this for x in iterable while condition]
"Do this for each x in iterable while condition remains true."

is an English-like expression. But your proposal isn't:

[this for x in iterable break if x is None]
"Do this for each x in iterable break if condition is true."

The "break if..." clause reads like a second statement. Now I realise that Python is not English and doesn't match English grammar, but still, this proposal doesn't flow off the tongue, it trips and stumbles because it feels like two statements merely shoe-horned into one. Python is, for the most part, an astonishingly beautiful language to read (at least for English speakers) and I'm afraid that "break if condition" in a comprehension is not beautiful.



-- 
Steven


More information about the Python-ideas mailing list