Alternative suggestion for conditional expressions (see PEP 308)

N

neblackcat

Would anyone like to comment on the following idea?

I was just going to offer it as a new PEP until it was suggested that
I post it here for comment & consideration against PEP 308.

I'm far from being a "language internist" (on Python or anything else)
so go easy on me if this is stupid - it just seemed quite elegant to
me as a relative newbie in town :)

I also havent got a clue whether this would be easy or even possible
to implement even if it was deemed a reasonable idea.

-----------------------------------------------------------------------

Abstract

Implement C-like conditional expressions, but in a more
flexible and "Python-like" way.

Motivation

The lack of conditional expressions combined with the lack
of a 'switch' statement in Python tends to result in lots
of 'if'/'elif' lines which often arent needed and which make
code more verbose and sometimes (where there are a lot of
them) harder to follow and more prone to mistakes than it
needs to be.

This PEP attempts to address that by proposing a
'Pythonesque' conditional expressions language feature
which fits well with existing language features
and the way that conditional expressions are often
simulated in current Python code by using temporary
tuples, lists or dictionaries.


Rationale

It is currently possible to simulate conditional
expressions in Python using constructs with
temporary tuples/lists, such as:

sColorName = ("Black","White")[Color == COLOR_WHITE]

This works, but has two disadvantages:

i) it involves packing and unpacking of a 'temporary'
tuple, eg. ("Black","White") in the above, which
has performance implications

ii) this in turn means that *all* of the possible
final values have to be valid (not just the one
which will be actually selected), which is often
not the case in a more real-world example, eg:

r = (DEFAULT_RESULT, Database.Results[index])
[IsValid(index)]

In the above, 'Database.Results[index]' isn't
defined to be valid if the 'index' value isn't,
which means that this technique cant be used.

Specification

I therefore suggest a variation on the above
syntax such as:

sColorName = ?("Black","White")[Color == COLOR_WHITE]

where the '?' identifies the immediately following
tuple as a "conditional tuple" in which the tuple is
not actually fully evaluated/stored. Instead,
only the element specified by the immediately following
index expression is evaluated and must be valid.

This syntax could also apply to other sequence types,
for example dictionaries:

sColorName = ?{COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green"}[ColorValue]

Again, the conditional sequence is not fully evaluated/stored,
so it doesnt matter if it is large and/or has members which
arent evaluated given an index value which doesnt actually
select them.

Obviously, the above are just simple examples.
 
P

Peter Hansen

neblackcat said:
Would anyone like to comment on the following idea?
Implement C-like conditional expressions, but in a more
flexible and "Python-like" way. [...]
I therefore suggest a variation on the above
syntax such as:

sColorName = ?("Black","White")[Color == COLOR_WHITE]

Funky uses of punctuation are about as non-Pythonic as you
can get, so I doubt this idea would make it past the post...

-Peter
 
L

Larry Bates

Seems that every one of the examples can be done
with a dictionary.

sColorName = ("Black","White")[Color == COLOR_WHITE]

use dictionary lookup:

colorNameDict={COLOR_WHITE:"White", COLOR_BLACK: "BLACK"}
sColorName=colorNameDict[Color]


and

sColorName = ?{COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green"}[ColorValue]


can be done as:

colorNameDict={COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green"}

sColorName=colorNameDict[ColorValue

you can also handle the "Default/undefined" case as:

colorNameDict={COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green"}

sColorname=ColorNameDict.get(ColorValue, defaultvalue)

or as I often do:

colorNameDict={COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green",
'unknown': 'Unknown'}

sColorname=colorNameDict.get(ColorValue, colorNameDict['unknown'])

Since these dictionaries should be defined once and never
inside a loop, I don't believe that performance is an issue.

You can also simulate a switch statement as follows:

def foo(args):
print "in foo"

def bar(args):
print "in bar"

def hee(args):
print "in hee"

def unknown(args):
print "in unknown"

switchDict={'one': foo, 'two': bar, 'three': hee,
'default': unknown}

switchvalue='one'
switchFunction=switchDict.get(switchvalue, switchvalue['default'])
switchFunction(args)

Again you get excellent performance because dictionary lookups
are blazingly fast. switchDict is only evaluated a single time
(or should be) and is easily extendable by simply adding more
switch values as keys.

Larry Bates
Syscon, Inc.

neblackcat said:
Would anyone like to comment on the following idea?

I was just going to offer it as a new PEP until it was suggested that
I post it here for comment & consideration against PEP 308.

I'm far from being a "language internist" (on Python or anything else)
so go easy on me if this is stupid - it just seemed quite elegant to
me as a relative newbie in town :)

I also havent got a clue whether this would be easy or even possible
to implement even if it was deemed a reasonable idea.

-----------------------------------------------------------------------

Abstract

Implement C-like conditional expressions, but in a more
flexible and "Python-like" way.

Motivation

The lack of conditional expressions combined with the lack
of a 'switch' statement in Python tends to result in lots
of 'if'/'elif' lines which often arent needed and which make
code more verbose and sometimes (where there are a lot of
them) harder to follow and more prone to mistakes than it
needs to be.

This PEP attempts to address that by proposing a
'Pythonesque' conditional expressions language feature
which fits well with existing language features
and the way that conditional expressions are often
simulated in current Python code by using temporary
tuples, lists or dictionaries.


Rationale

It is currently possible to simulate conditional
expressions in Python using constructs with
temporary tuples/lists, such as:

sColorName = ("Black","White")[Color == COLOR_WHITE]

This works, but has two disadvantages:

i) it involves packing and unpacking of a 'temporary'
tuple, eg. ("Black","White") in the above, which
has performance implications

ii) this in turn means that *all* of the possible
final values have to be valid (not just the one
which will be actually selected), which is often
not the case in a more real-world example, eg:

r = (DEFAULT_RESULT, Database.Results[index])
[IsValid(index)]

In the above, 'Database.Results[index]' isn't
defined to be valid if the 'index' value isn't,
which means that this technique cant be used.

Specification

I therefore suggest a variation on the above
syntax such as:

sColorName = ?("Black","White")[Color == COLOR_WHITE]

where the '?' identifies the immediately following
tuple as a "conditional tuple" in which the tuple is
not actually fully evaluated/stored. Instead,
only the element specified by the immediately following
index expression is evaluated and must be valid.

This syntax could also apply to other sequence types,
for example dictionaries:

sColorName = ?{COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green"}[ColorValue]

Again, the conditional sequence is not fully evaluated/stored,
so it doesnt matter if it is large and/or has members which
arent evaluated given an index value which doesnt actually
select them.

Obviously, the above are just simple examples.
 
J

Jeff Shannon

Larry said:
Seems that every one of the examples can be done
with a dictionary.

While that may be true of the examples given here, it's not true of all
cases. The one possible real advantage of a conditional expression, as
I see it, is shortcutting. In cases where the conditionally-executed
code has side effects, it can be important to ensure that *only* one
branch is evaluated; creating an actual dictionary (whether permanent or
temporary) will execute *all* branches. For example, using neblackcat's
proposed syntax (even though I'm *not* fond of it):

nextline = ?(FileOne.readline(), FileTwo.readline())[whichfile]

In this case, since reading a line from a file advances the filepointer,
executing both will result in a line being "lost" from the non-selected
file. (I realize that in this specific example, one could use the
conditional to select a file object and call readline() on the result of
the conditional; I could also construct examples in which entirely
different operations-with-side-effects are performed on each branch so
that refactoring in that way isn't practical, or one branch is a string
literal and the other has some side-effect, or whatever.)

Note that, while I do think it's important to be aware of this
distinction, I'm not advocating the proposed syntax (I'm rather leery of
using punctuation like that, as Peter Hansen mentions) or even
necessarily advocating for a conditional expression at all. I remain
unconvinced of the need for an expression to do what can already be done
with statements -- a conditional expression may be more concise, but
conciseness more often hampers readability than helps it, and
readability is very important. I've heard the arguments that using a
multiline if/else construct in order to set up parameters for a function
call is ugly, but I personally find that to be no worse than the
ugliness of conditional expressions...

Jeff Shannon
Technician/Programmer
Credit International
 
P

Phil Frost

Larry said:
Seems that every one of the examples can be done
with a dictionary.

While that may be true of the examples given here, it's not true of all
cases. The one possible real advantage of a conditional expression, as
I see it, is shortcutting. In cases where the conditionally-executed
code has side effects, it can be important to ensure that *only* one
branch is evaluated; creating an actual dictionary (whether permanent or
temporary) will execute *all* branches. For example, using neblackcat's
proposed syntax (even though I'm *not* fond of it):

nextline = ?(FileOne.readline(), FileTwo.readline())[whichfile]

...

Evaluation of the expressions could be deferred by using callables, like
so:

next = (lambda: fileone.readline(), lambda filetwo.readline())[which]()

Or since in this example both sides of the tuple are already callables:

next = (fileone.readline, filetwo.readline)[which]()

But then really, is writing:

if which: next=fileone.readline()
else: next=filetwo.readline()

all that bad?
 
C

Colin J. Williams

I was inclined to support PEP 308, but now wonder whether it's really
needed.

sColorName = ("Black","White")[Color == COLOR_WHITE]

Could be:

sColorName = Color == COLOR_WHITE and "Black" or "White"

which takes avantantage of the short circuit evaluation

sColorName= color == colorwhite and "white" or garbage

This gives the correct result when only the first case needs to be
evalutated.

Colin W.
Would anyone like to comment on the following idea?

I was just going to offer it as a new PEP until it was suggested that
I post it here for comment & consideration against PEP 308.

I'm far from being a "language internist" (on Python or anything else)
so go easy on me if this is stupid - it just seemed quite elegant to
me as a relative newbie in town :)

I also havent got a clue whether this would be easy or even possible
to implement even if it was deemed a reasonable idea.

-----------------------------------------------------------------------

Abstract

Implement C-like conditional expressions, but in a more
flexible and "Python-like" way.

Motivation

The lack of conditional expressions combined with the lack
of a 'switch' statement in Python tends to result in lots
of 'if'/'elif' lines which often arent needed and which make
code more verbose and sometimes (where there are a lot of
them) harder to follow and more prone to mistakes than it
needs to be.

This PEP attempts to address that by proposing a
'Pythonesque' conditional expressions language feature
which fits well with existing language features
and the way that conditional expressions are often
simulated in current Python code by using temporary
tuples, lists or dictionaries.


Rationale

It is currently possible to simulate conditional
expressions in Python using constructs with
temporary tuples/lists, such as:

sColorName = ("Black","White")[Color == COLOR_WHITE]

This works, but has two disadvantages:

i) it involves packing and unpacking of a 'temporary'
tuple, eg. ("Black","White") in the above, which
has performance implications

ii) this in turn means that *all* of the possible
final values have to be valid (not just the one
which will be actually selected), which is often
not the case in a more real-world example, eg:

r = (DEFAULT_RESULT, Database.Results[index])
[IsValid(index)]

In the above, 'Database.Results[index]' isn't
defined to be valid if the 'index' value isn't,
which means that this technique cant be used.

Specification

I therefore suggest a variation on the above
syntax such as:

sColorName = ?("Black","White")[Color == COLOR_WHITE]

where the '?' identifies the immediately following
tuple as a "conditional tuple" in which the tuple is
not actually fully evaluated/stored. Instead,
only the element specified by the immediately following
index expression is evaluated and must be valid.

This syntax could also apply to other sequence types,
for example dictionaries:

sColorName = ?{COLOR_RED:"Red",
COLOR_BLUE:"Blue",
COLOR_GREEN:"Green"}[ColorValue]

Again, the conditional sequence is not fully evaluated/stored,
so it doesnt matter if it is large and/or has members which
arent evaluated given an index value which doesnt actually
select them.

Obviously, the above are just simple examples.
 
M

Mark 'Kamikaze' Hughes

Colin J. Williams said:
I was inclined to support PEP 308, but now wonder whether it's really
needed.
sColorName = ("Black","White")[Color == COLOR_WHITE]
Could be:
sColorName = Color == COLOR_WHITE and "Black" or "White"
which takes avantantage of the short circuit evaluation
sColorName= color == colorwhite and "white" or garbage
This gives the correct result when only the first case needs to be
evalutated.

This works only as long as the results of the (true, false) choices
both evaluate true:
.... print "eval foo"
.... return "foo"
........ print "eval bar"
.... return "bar"
....eval bar
'bar'eval foo
'foo'

Looks good, right?
.... print "eval foo"
.... return ""
....eval bar
'bar'eval foo
eval bar
'bar'

Alas. Not only does it evaluate both, causing who-knows-what side
effects, it returns the wrong answer. Your Python nuclear missile
launch program goes out of control. All die. O the embarassment.

This is why we do actually need a ternary. It's not that the language
lacks tools for choosing now, but that this behavior is so very easy to
get wrong, and leads to very subtle bugs.

The PEP 308 vote showed that at least 84% of the Python users wanted a
ternary. A second vote between the four major choices would have
obviously ended us up with the (if <cond1>: <expr1> elif <cond2>:
<expr2> ... else: <exprN>) form, which is about as Pythonically
unambiguous as you can get.

At the time, my opinion was that I didn't need a ternary often enough
to care. Sometimes I want to modify some math depending on a boolean,
and in that case I can just treat it as 0 or 1:
8

And the rest of the time, it's easy (if tedious) to just write:
.... else: a=bar()
....
eval foo''

However, the ease of doing the and...or and ()[] versions wrong, and
how unbelievably ugly the latter is, have changed my mind from "this is
not important" to "this is something Python really does need, after
all".
 
?

=?iso-8859-15?Q?Pierre-Fr=E9d=E9ric_Caillaud?=

Flame suit on.

- add a switch-case statement :
I do miss a switch-case in Python.
The interpreter can't really optimize if/elif as a dictionary lookup,
because what if there is a AND or OR clause, or if the object has defined
a __cmp__ method with side effects ? This would be pervert...

switch color:
case 1:
name = 'red'
case 2:
name = 'blue'
case 3:
case 4:
name = 'colorblind'

Could use a 'break' (like in C) or not (cleaner).

- add a switch-case statement which can return a value.
Problem : you need a new keyword for that, which is a bad thing... and it
starts to look like lisp...

I think the ternary operator is a half-baked solution to a more general
problem ; it is useful as a little shortcut like " x ? 'Yes' : 'No' but
doing more with it leads to obfuscation.

Still, something lacks in Python... just like the break-n-levels.
 
M

Mark 'Kamikaze' Hughes

Pierre-Frédéric Caillaud said:
Flame suit on.
- add a switch-case statement :
I do miss a switch-case in Python.
The interpreter can't really optimize if/elif as a dictionary lookup,
because what if there is a AND or OR clause, or if the object has defined
a __cmp__ method with side effects ? This would be pervert...
switch color:
case 1:
name = 'red'
case 2:
name = 'blue'
case 3:
case 4:
name = 'colorblind'
Could use a 'break' (like in C) or not (cleaner).
- add a switch-case statement which can return a value.
Problem : you need a new keyword for that, which is a bad thing... and it
starts to look like lisp...

Simpler version that already works:
colornames = {
1: "red",
2: "blue",
}
print colornames.get(color, "colorblind")
I think the ternary operator is a half-baked solution to a more general
problem ; it is useful as a little shortcut like " x ? 'Yes' : 'No' but
doing more with it leads to obfuscation.

It's more for selecting between function calls than for constants. A
common idiom with a ternary in Java is:
value = test == null ? null : test.something();

That doesn't happen as often in Python, but choosing between different
methods depending on a flag happens more often.
Still, something lacks in Python... just like the break-n-levels.

At least there's a consistent and easy way to do that. Use
exceptions, or extract your inner loops into a method, and return when
you want to "break out".
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top