[Python-ideas] "else" expression ":"

44 views
Skip to first unread message

Peter Norvig

unread,
Apr 13, 2013, 2:24:02 PM4/13/13
to python-ideas



Beginners will often write code like this:

if val > 0:
    return +1
elif val < 0:
    return -1
elif val == 0:
    return 0

Now if you did this in Java, the compiler would produce an error saying that there is an execution path that does not return a value. Python does not give an error message, but it would be considered more idiomatic (and slightly more efficient) to have just "else:" in the third clause.  

Here's an idea to address this.  What do you think of the syntax

     "else" expression ":"

for example:

if val > 0:
    return +1
elif val < 0:
    return -1
else val == 0:
    return 0

with the interpretation:

if val > 0:
    return +1
elif val < 0:
    return -1
else:
    assert val == 0
    return 0

I have to say, I'm uncertain.  I'm not sure this is even a good idea at all, and I'm not sure if it should translate into  "assert expression" or whether it should be "if not expression: raise ValueError". What do you think?

-Peter Norvig

David Mertz

unread,
Apr 13, 2013, 3:01:03 PM4/13/13
to Peter Norvig, python-ideas
On Apr 13, 2013, at 11:24 AM, Peter Norvig wrote:
> Here's an idea to address this. What do you think of the syntax
> "else" expression ":"
> for example:
> if val > 0:
> return +1
> elif val < 0:
> return -1
> else val == 0:
> return 0

I often write code like:

if cond1:
doThing1()
elif cond2:
doThing2()
# ... more steps here ...
return something

I guess implicitly I think of this as a less verbose form of:

if cond1:
doThing1()
elif cond2:
doThing2()
else:
pass # No need to do anything if not cond1 and not cond2

That is, the situation where every block of the condition ends in a return statement is a special case, and by no means universal to the use of if/elif/else. In particular, not every time I use if/elif do I want a "catch the remaining cases" block, since it is often only in some enumerated special circumstances I want any processing to occur in the compound block.

I think the "else with boolean" is definitely readable, modulo exactly what exception is raised if it is violated. However, it feels like the explicit 'assert' statement in those cases where we expect exhaustive conditions is already available. Moreover, we can always add a final else to document our belief that conditions are exhaustive:

if val > 0:
return +1
elif val < 0:
return -1
elif val == 0:
return 0
else:
raise ValueError("'val' is not negative, positive, or zero! Check the properties of arithmetic")


--
mertz@ THIS MESSAGE WAS BROUGHT TO YOU BY: v i
gnosis Postmodern Enterprises s r
.cx MAKERS OF CHAOS.... i u
LOOK FOR IT IN A NEIGHBORHOOD NEAR YOU g s



_______________________________________________
Python-ideas mailing list
Python...@python.org
http://mail.python.org/mailman/listinfo/python-ideas

Terry Jan Reedy

unread,
Apr 13, 2013, 3:21:33 PM4/13/13
to python...@python.org
On 4/13/2013 2:24 PM, Peter Norvig wrote:
> Beginners will often write code like this:
>
> if val > 0:
> return +1
> elif val < 0:
> return -1
> elif val == 0:
> return 0

So might a Python expert who knows that all three tests could return
False for instances of some class. For instance, a fuzzy zero, or an
interval that includes 0.

Or someone who like to be explicit about the appropriate guard for each
return.

> Now if you did this in Java, the compiler would produce an error saying
> that there is an execution path that does not return a value.

Not relevant since in Python all paths that end either raise or return.

> Python does not give an error message,

because it is not an error.

> but it would be considered more idiomatic (and slightly more
> efficient) to have just "else:" in the third clause.

Only when the last condition is the negation of the conjunction of the
first two. Or when it is broader than that. I might actually write
something like

else: # val = 0

to document the simplified negated conjunction, which in this case is
the appropriate guard. Being explicit, at least with a comment, makes it
easier for someone to re-order the branches, should there be reason to.

Python routinely allows correct but unidiomatic and inefficient code.

> Here's an idea to address this.

I do not see that there is a problem to fix ;-).

> What do you think of the syntax
> "else" expression ":"

A confusing solution to a non-problem ;-).

--
Terry Jan Reedy

Mark Lawrence

unread,
Apr 13, 2013, 3:34:32 PM4/13/13
to python...@python.org
Big -1 from me, if it ain't broke don't fix it.


--
If you're using GoogleCrap™ please read this
http://wiki.python.org/moin/GoogleGroupsPython.

Mark Lawrence

Shane Green

unread,
Apr 13, 2013, 3:56:11 PM4/13/13
to David Mertz, python-ideas, Peter Norvig
That's a bad example because the comparisons would probably have raised a ValueError or TypeError for any, but I think being explicit is by far preferable, and just as concise, if not more so because you control the exception: 


elif val < 0: 
return 0
elif value != 0: 
raise ValueError()
return value


Of course you likely will have gotten a ValueError or TypeError already if you've done gt/lt comparisons on a value that turns out to also not be 0… If you're going to validate the data type eventually, why not do it at the top?



Shane Green 

Greg Ewing

unread,
Apr 13, 2013, 7:07:38 PM4/13/13
to python-ideas
Peter Norvig wrote:

> Beginners will often write code like this:
>
> if val > 0:
> return +1
> elif val < 0:
> return -1
> elif val == 0:
> return 0
>
Here's an idea to address this. What do you think of the syntax
>
> "else" expression ":"

This will do nothing to help said beginner. He will continue
to write "elif val == 0:", blissfully unaware that there could
be another case that he hasn't thought of. If he had the
presence of mind to realise that, he would have written
something safer in the first place.

--
Greg

Steven D'Aprano

unread,
Apr 13, 2013, 10:36:59 PM4/13/13
to python...@python.org
On 14/04/13 04:24, Peter Norvig wrote:
> Beginners will often write code like this:
>
> if val > 0:
> return +1
> elif val < 0:
> return -1
> elif val == 0:
> return 0
>
> Now if you did this in Java, the compiler would produce an error saying
> that there is an execution path that does not return a value.

There's one difference between the languages right there: there is no such
case for Python. If you pass something that doesn't match any of the three
cases, say a NAN, the function will return None.


> Python does
> not give an error message, but it would be considered more idiomatic (and
> slightly more efficient) to have just "else:" in the third clause.

Also incorrect, in a language which supports NANs, as Python does. (And Java,
I believe, which may be why Java correctly tells you that there is a path
with no return result.)



> Here's an idea to address this. What do you think of the syntax
>
> "else" expression ":"


I don't think it will help beginners, and for more experienced programmers,
I don't think it is of much benefit over an explicit

else:
assert expression, "message if the assert fails"

(or an explicit ValueError test, if more appropriate).


[...]
> I have to say, I'm uncertain. I'm not sure this is even a good idea at
> all, and I'm not sure if it should translate into "assert expression" or
> whether it should be "if not expression: raise ValueError". What do you
> think?

I think that there's no one right answer. For some code, an assertion will
be correct, and for others, an explicit test and ValueError (or some other
exception!) will be correct. Neither is so obviously more common that Python
should introduce syntax to favour one over the other.


--
Steven

Shane Green

unread,
Apr 13, 2013, 10:54:58 PM4/13/13
to Steven D'Aprano, python...@python.org
Ah, yes, I should clarify that when I suggested: 

elif val < 0: 
  return -1
elif value != 0:
  raise ValueError() 

I did NOT mean to propose it as the syntactical translation of anything; I was suggesting that beginning programmers should use that instead of the first approach.  I would say that, between: 

explicit is always better than implicit; 
in the face of ambiguity, refuse the temptation to buses; 
there should be one, and preferably only one, way to do it; and 
special cases aren't special enough to break rules. 

Well, I like the way this one works now… 

Nick Coghlan

unread,
Apr 14, 2013, 6:31:09 AM4/14/13
to Peter Norvig, python-ideas
I think the difference between:

if val > 0:
return +1
elif val < 0:
return -1
else val == 0:
return 0

and:

if val > 0:
return +1
elif val < 0:
return -1
elif val == 0:
return 0

is far too subtle to be helpful. If an if/elif chain is expected to be
exhaustive and you want to ensure it remains that way during ongoing
maintenance, you can already do:

if val > 0:
return +1
elif val < 0:
return -1
elif val == 0:
return 0
else:
raise RuntimeError("Unhandled input: {!r:100}".format(val))

(with the "else:" being optional if this is the last statement in a function)

In general, though, Python already decided on its answer to this
question by always allowing a final "return None" to be implicit, even
when there are explicit return statements elsewhere in the function.

Cheers,
Nick.

--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia

Shane Green

unread,
Apr 14, 2013, 7:48:08 AM4/14/13
to Nick Coghlan, python-ideas, Peter Norvig
I think that, if something special were added for this case, it may be best for it to be not overload the so incredibly well defined through the ages if/elif/ or else clauses, perhaps adding something new, like: 

elifassert val==0:
retur…

…you know, I've got say, I don't really recall finding myself in this situation very often in the first place.  It seems like the most productive solution might be to plant an easter egg that suggests some of these alternative approaches!

Serhiy Storchaka

unread,
Apr 14, 2013, 7:58:49 AM4/14/13
to python...@python.org
On 13.04.13 22:21, Terry Jan Reedy wrote:
> On 4/13/2013 2:24 PM, Peter Norvig wrote:
>> Beginners will often write code like this:
>>
>> if val > 0:
>> return +1
>> elif val < 0:
>> return -1
>> elif val == 0:
>> return 0
>
> So might a Python expert who knows that all three tests could return
> False for instances of some class. For instance, a fuzzy zero, or an
> interval that includes 0.

Or float('nan').

Yaroslav Fedevych

unread,
Apr 18, 2013, 2:29:12 AM4/18/13
to python...@python.org
An obligatory read for anyone who comes up with ideas like this: http://thedailywtf.com/Articles/ButAnything-Can-Happen!.aspx

Philipp A.

unread,
Apr 18, 2013, 10:21:06 AM4/18/13
to Yaroslav Fedevych, Python-Ideas
no, this is different; it’s not for booleans, but for assertions, and could be used for e.g. exhaustive switches, e.g. it would make the first test in the following unnecessary:

if spam not in {"a", "b", "c"}:
    throw new ValueError("spam {} should be a, b, or c!".format(spam))
if spam == "a":
    foo()
elif spam == "b":
    bar()
else:
    baz()

i’m not saing i support the idea though. i’d rather see scala-like extensible pattern matching. that would solve this, the eternal switch debate, and more (using extractors).

is there a proposal for pattern matching? if not, i’ll come up with one ;)


2013/4/18 Yaroslav Fedevych <yaro...@fedevych.name>
An obligatory read for anyone who comes up with ideas like this: http://thedailywtf.com/Articles/ButAnything-Can-Happen!.aspx

Chris Angelico

unread,
Apr 18, 2013, 10:33:28 AM4/18/13
to python-ideas
On Fri, Apr 19, 2013 at 12:21 AM, Philipp A. <flying...@web.de> wrote:
> no, this is different; it’s not for booleans, but for assertions, and could
> be used for e.g. exhaustive switches, e.g. it would make the first test in
> the following unnecessary:
>
> if spam not in {"a", "b", "c"}:
> throw new ValueError("spam {} should be a, b, or c!".format(spam))
> if spam == "a":
> foo()
> elif spam == "b":
> bar()
> else:
> baz()

Or alternatively, you could write it as:

if spam == "genuine watch":
foo()
elif spam == "buy a college degree":
bar()
elif spam == "rich guy wants to move money offshore":
baz()
else:
raise ValueError("Unrecognized spam '%s'!" % spam)

That removes the need to pre-check and match your if block.

ChrisA

Serhiy Storchaka

unread,
Apr 18, 2013, 4:04:29 PM4/18/13
to python...@python.org
On 18.04.13 17:33, Chris Angelico wrote:
> On Fri, Apr 19, 2013 at 12:21 AM, Philipp A. <flying...@web.de> wrote:
>> no, this is different; it’s not for booleans, but for assertions, and could
>> be used for e.g. exhaustive switches, e.g. it would make the first test in
>> the following unnecessary:
>>
>> if spam not in {"a", "b", "c"}:
>> throw new ValueError("spam {} should be a, b, or c!".format(spam))
>> if spam == "a":
>> foo()
>> elif spam == "b":
>> bar()
>> else:
>> baz()
>
> Or alternatively, you could write it as:
>
> if spam == "genuine watch":
> foo()
> elif spam == "buy a college degree":
> bar()
> elif spam == "rich guy wants to move money offshore":
> baz()
> else:
> raise ValueError("Unrecognized spam '%s'!" % spam)
>
> That removes the need to pre-check and match your if block.

Or alternative:

alternatives = {
"genuine watch": foo,
"buy a college degree": bar,
"rich guy wants to move money offshore": baz,
}
try:
alternative = alternatives[spam]
except KeyError:
raise ValueError("Unrecognized spam '%s'!" % spam)
alternatives[spam]()
Reply all
Reply to author
Forward
0 new messages