[Python-ideas] if condition: break idiom
Arnaud Delobelle
arnodel at googlemail.com
Sun Sep 21 20:50:12 CEST 2008
On 21 Sep 2008, at 17:48, Josiah Carlson wrote:
> On Sat, Sep 20, 2008 at 1:26 PM, Arnaud Delobelle
> <arnodel at googlemail.com> wrote:
>>
>> On 20 Sep 2008, at 20:44, Josiah Carlson wrote:
>>
>>>> On Sat, Sep 20, 2008 at 11:35 AM, Arnaud Delobelle
>>>>>
>>>>> [...] I think that this would look better:
>>>>>
>>>>> while True:
>>>>> do something
>>>>> break if condition
>>>>> do something else
>>>>>
>>>>
>>>> It gets worse ;)
>>>
>>> break if condition
>>>
>>> Also implies...
>>>
>>> continue if condition
>>>
>>
>> That's true, why not? I don't use use continue so much but it seems
>> logical.
>>
>>> Never mind
>>>
>>> break if condition else continue
>>> continue if condition else break
>>>
>>
>> No that would be silly.
>
> Why? It follows directly from conditional expressions as well as your
> proposed syntax?
>
I don't think it does. This has nothing to do with conditional
expressions.
>>> Because who would want to write...
>>> break if condition
>>> continue
>>>
>>> or
>>> continue if condition
>>> break
>>>
>>
>> The problem is: when would you need to do this? I pointed out in my
>> original post that almost half of uses of 'break' in the py3k
>> python source
>> come immediately after an 'if'. Now I have never used or seen this:
>>
>> if condition:
>> break
>> continue
>
> Just because *you've* never seen it, doesn't mean it shouldn't be a
> valid control structure.
>
What I'm saying is that it doesn't occur often enough to be worried
about it. OTOH, 'if condition: break' occurs very commonly.
[...]
> I would then point out that the cases that you are trying to fix,
> 'break if condition', you inadvertently hide the *flow control* in a
> mass of words. Break and continue are currently happy little
> single-word statements. Even when trailing on the same line as an
> 'if', there is a colon separating them from the if and condition. I
> would argue that this punctuation is critical for understanding the
> idea "something conditional is happening here to your flow control".
> Without that colon, break/continue get to be the only two statements
> that get an optional 'if' suffix, whose only purpose seems to be to
> reduce line count.
>
Reducing line count was definitely not my aim (see just below)
>> What would be the point of spelling it 'break if condition else
>> continue'?
>> It would even defeat the idea behind my suggestion, which is to
>> make the
>> structure of a loop more obvious when you scan code: you can spot
>> the 'break
>> if' (and 'continue if'!) statements just by scanning the left hand
>> side of
>> the loop body.
>
> But this doesn't help in that. Break and continue are *already*
> immediately to the right of whitespace (unless someone tries to make
> it a single line), and while the *simple* case of...
>
> for ... in ...:
> ...
> if condition:
> break
> ...
>
> ... is common, that doesn't mean that the following isn't ...
>
You forget:
if condition: break
> for ... in ...:
> ...
> if condition:
> ...
> if other_condition:
> break
> ...
> ...
>
> Which isn't greatly improved by your suggestion.
There's some truth in this.
>>> But if we can break or continue, why not others? What's wrong
>>> with a
>>> raise (especially if we surround everything with parens...)?
>>>
>>> (raise Exception("X")) if condition
>>>
>>> Never mind assert, yield, throw, return, ... I hope this horse is
>>> dead
>>> now.
>>>
>>
>> You have implied one reason why not: raise, assert, yield, throw,
>> return all
>> take an argument which can be an if-expression, so this would make
>> those
>> statements difficult to parse if you are human (and I suspect
>> impossible if
>> you are the Python parser).
>
> No, I pointed out a syntax that is unambiguous. How do I know it is
> unambiguous? Because it's used by the yield *expression* in Python
> 2.5 and later:
>
> x = (yield y) + 1
>
> Though maybe that's a vote against yield, because the rest are
> statements and don't return anything.
>
Yes, they're statements, I wouldn't want them to be surrounded by
parentheses. I only proposed "break if" (and "continue if", perhaps),
I don't see why every statement should be equipped with a trailing
"if" as a necessary consequence.
>> The fact that neither break nor continue take arguments makes them
>> very
>> 'poor' in the amount of meaning they convey. That's why a lot of
>> people
>> feel like writing the following in one single line.
>>
>> if condition: break
>
> It's not a question of amount of meaning, it's a question of context.
> I've heard explanations at least as much saying that they thought that
> not having an indented trailer makes the code "prettier", "shorter",
> "easier to understand" as I've heard the opposite from reviewers
> arguing against the single-line if idiom.
>
if condition: break
on a single line happens a lot, whether liked by reviewers or not.
>> Moreover, because we don't have a 'do .. while' construct, 'break'
>> is more
>> important than in other languages to shape loops (especially while
>> loops),
>> so I thought it would be useful to make it easier to spot. The same
>> argument can be made about 'continue' of course.
>
> Perfectly reasonable, but then the base isn't so much...
>
> while 1:
> ...
> if condition: break
> ...
>
> it's really...
>
> while 1:
> ...
> if condition: break
>
I don't agree with that: the absence of do .. while liberates the loop
construct in python from its constraints and the first form above
becomes more common.
> Which *has* an idiom in Python.
>
> first = 1
> while first or condition:
> first = 0
> ...
>
I would not use this, not because it is slower, but because it is
uglier.
> And before you come back with "but that's not as fast", I'll point out
> that an assignment to a local and an or computation are pretty darn
> fast. With psyco; even faster:
>
>>>> def test1(n):
> ... i = 0
> ... while 1:
> ... i += 1
> ... if i >= n: break
> ...
>>>> def test2(n):
> ... i = 0
> ... first = 1
> ... while first or n > i:
> ... first = 0
> ... i += 1
> ...
>>>> t = time.time(); test1(10000000); time.time()-t
> 1.0160000324249268
>>>> t = time.time(); test2(10000000); time.time()-t
> 1.1559998989105225
>>>> import psyco
>>>> psyco.full()
>>>> t = time.time(); test1(1000000000); time.time()-t
> 1.4059998989105225
>>>> t = time.time(); test2(1000000000); time.time()-t
> 1.4060001373291016
>
My tests:
>>> t1=timeit.Timer("test1(10000000)", "from loops import
test1").timeit(5)
>>> t2=timeit.Timer("test2(10000000)", "from loops import
test2").timeit(5)
>>> t1, t2
(5.3120660781860352, 7.4985768795013428)
>>> t2/t1
1.4116121240084343
test1 is 41% faster, that is not negligible. I don't think the fact
that psyco optimises the code is a valid argument.
> Worst-case, 15% more time without psyco for the *simplest* possible
> loop. Less difference as the body of the while increases. With
> psyco: no difference.
>
> But what about, "that's not as pretty as 'break if condition'"? Ok,
> it's not as pretty. But I would then point out that at least now, you
> don't have one of those "while 1:" loops that can raise eyebrows.
>
Well, they don't raise my eyebrows :)
> - Josiah
Anyway, there hasn't been a flurry of positive responses so far so I
don't think this is going to go much further than this reply...
--
Arnaud
More information about the Python-ideas
mailing list