|
|
 |
|
Title: Simulating the ternary operator in Python
Submitter: Jürgen Hermann
(other recipes)
Last Updated: 2001/03/19
Version no: 1.0
Category:
Text
|
|
4 vote(s)
|
|
|
|
Description:
People coming from C, C++ or Perl might miss the so-called ternary operator ?: (condition ? then-expr : else-expr). It's most often used for avoiding several lines of code and temporary variables for very simple decisions, like printing the plural form of words after a counter (see example code).
There are two ways to get the same effect in Python: selecting one of two values from a tuple, or using the special behaviour of the "and" and "or" operators in Python. The second method has the advantage that only ONE of the two possible expressions is evaluated, and is thus more close to the behaviour of ?: as defined by C.
Source: Text Source
for i in range(1,3):
if i == 1:
plural = ''
else:
plural = 's'
print "The loop ran %d time%s" % (i, plural)
for i in range(1,3):
print "The loop ran %d time%s" % (i, ('','s')[i != 1])
for i in range(1,3):
print "The loop ran %d time%s" % (i, i != 1 and 's' or '')
Discussion:
|
|
Add comment
|
|
Number of comments: 15
Accomodating false val_1 while keeping shortcircuit behaviour, Hamish Lawson, 2001/03/29
As has been noted, the (cond and val_1 or val_2) technique suffers from the fact that val_1 can't be false, while the disadvantage of the function approach is that it doesn't exhibit shortcircuit behaviour. However there is an elaboration of the and-or technique which does accomodate val_1 being false while still exhibiting shortcircuit behaviour (though perhaps it can't claim to be pretty!):
(cond and [val_1] or [val_2])[0]
Enclosing val_1 in a list ensures that it will be true.
Add comment
Important note and another approach, Michael Chermside, 2001/03/28
It is *very* important to point out that ( cond and val_1 or val_2 ) is not the
same as ( cond ? val_1 : val_2 ) unless val_1 is guaranteed not to be false.
Because of this, I would recomend including yet another
approach... that of defining a utility function. Here's
mine:
def if_else(condition, trueVal, falseVal):
if condition:
return trueVal
else:
return falseVal
Then I just use it in code as
x = if_else( y>3, 3*y+1, None );
And the disadvantage here is that there is no
"short-circuiting"... both options are evaluated
even if they aren't needed (in the example above,
if y==2, then 3*y+1 will still be calculated).
Add comment
iif only you had chosen a better name ;-), Clark Updike, 2004/11/08
Just kidding. I use the exact same utility function and find it much cleaner than any of the approaches in the recipe (maybe it's just me). I chose the name 'iif' because that's what it is called in VB. Whoddathunk I'd ever use VB for inspiration :-)
Add comment
One more variation, Brent Burley, 2001/03/29
Here's a technique that, while it isn't equivalent to a ? b : c, is
often applicable in the same situations:
# multiply by result of condition
for i in range(1,3):
print "The loop ran %d time%s" % (i, 's' * (i != 1))
Add comment
Hamish Lawson, 2001/03/27
It may be worth noting that the construct "a and b or c" only works as a simulation of the ternary operator if the value of b is always true (i.e. not any of zero, the empty string, the empty list or None).
Add comment
simulating the ternary operator, Lloyd Goldwasser, 2001/06/29
The Algorithm category contains an entry by Alex Martelli on exactly this topic, with at least two solutions not included here...
Add comment
a? b : c emulation is Python FAQ 4.16, Stefan Schwarzer, 2002/01/11
There's also an Python FAQ on the topic:
http://www.python.org/cgi-bin/faqw.py?query=ternary&querytype=simple&casefold=yes&req=search
Note that the short circuit behaviour might be really necessary if evaluating b or c
has major side effects such as deleting a file. If that's the case I would rather use
a plain if-else construct than using (a and [b] or [c])[0] from the FAQ.
If there are no side effects I would prefer (b, c)[not a] or still the if-else thing.
Add comment
Can also use the overloaded multiplication operator for strings, Philip Kromer, 2002/07/06
You can also do
's'*(i!=1)
which is as close to obfuscated python as we can get.
Add comment
a bound lambda solution, Martin Freedman, 2005/03/26
In the Python FAQ 1.2.11 "Is there an equivalent of C's "?:" ternary operator?" the function solution given is
def q(cond,on_true,on_false):
if cond:
if not isfunction(on_true): return on_true
else: return apply(on_true)
else:
if not isfunction(on_false): return on_false
else: return apply(on_false)
You need to lambda: b or c if they are functions to prevent execution. However apply() is deprecated since release 2.3.
A much simpler and more efficient solution I use is
q=lambda a,b,c: (a and [b] or [c])[0]
you still need to lambda: b or c functions but it does not use deprecated functions.
Add comment
update, Martin Freedman, 2005/03/26
First following stefan's comment I now use
q=lambda a,b,c: (b,c)[not a]
and secondly because this is a short circuit and the arguments are not passed to another function, you dont need to lambda: b or c if it is a fucntion to prevent side effects!
Add comment
update to update, Martin Freedman, 2005/03/26
If only I could edit my comments :-( - You still to lambda b or c if fucntions to avoid side effects.
Add comment
A more flexible version, Christopher Dunn, 2005/05/05
import inspect
# inspect.isfunction would not check for C functions.
def if_else_apply(condition, ifTrue, ifFalse, *args, **kwds):
""Apply ifTrue or ifFalse to args, but not both.
>>> if_else_apply(False, sys.stderr.write, sys.stdout.write, 'No error')
No error
"""
if condition:
if not inspect.isroutine(ifTrue): return ifTrue
else: return ifTrue(*args, **kwds)
else:
if not inspect.isroutine(ifFalse): return ifFalse
else: return ifFalse(*args, **kwds)
Add comment
better ternary operator?, Rick Graves, 2005/06/10
Hey,
I got the idea for this function from code in "Python and Tkinter Programming" by John E. Grayson:
def ImIf( bCondition, uTrue, uFalse ):
#
from operator import truth
#
return ( uFalse, uTrue )[ truth( bCondition ) ]
"ImIf" is for Immediate IF, which is what Microsoft calls it, maybe someone else might call it that, too.
I believe it is truly and demonstrably "lazy". One can test it with these:
def SayTrue(): print "True"
def SayFalse(): print "False"
so
ImIf( Value, SayTrue, SayFalse )()
prints either "True" or "False" but not both.
I hope this is helpful.
Rick Graves
Add comment
more about ternary operator, Rick Graves, 2005/06/13
This was my first post to the cookbook or similar, and I have learned that I should read the prior postings carefully before adding my own. I had recently finished reading the second edition of the hard copy cookbook, and the ternary operator presented there did not hit the spot for me. I was a little excited after seeing code in "Python and Tkinter Programming" that could be used in a function that would hit the spot.
The file into which I would logically put my function already imported "truth" as a global, so I did not think hard about how to implement the function without it. It is obviously better to use "not", as in Martin Freedman's lambda implementation, as "not" does not need to be imported. So my function could become
def ImIf( bCondition, uTrue, uFalse ):
#
return ( uTrue, uFalse )[ not bCondition ]
So I see two related issues, a) which is better, an anonymous lambda or named function, and b) if using a named function, what should be the name.
Although I am one of those who prefer a function named with def over a lambda any day, for "the ternary operator," I personally would rather call an existing function with a short, descriptive name than type out the lambda line every time I wanted this function.
The name "ternary" has to do with 3, as the function takes 3 parameters/arguments, which seems to be unusual in the C world. But I have lots of functions that take 3 parameters/arguments, so the name "ternary" does not seem descriptive to me. From that standpoint, "immediate if" seems more descriptive, but I admit "immediate if" is already etched into my brain.
But I am not pushing "immediate if" -- if you want to have a named function rather than type out an anonymous lambda, use any name that works for you.
Add comment
ternary *operator* and 2.5, Jason Yamada-Hanff, 2007/09/26
I noticed this recipe/thread while browsing for python2.4 ternary-like behavior
It's probably now useful to note that python 2.5 has added a ternary operator of the form:
caseTrue if condition else caseFalse
Also, the name 'ternary' does indeed refer to the 3 arguments taken in the operator. Note, though, that it is descriptive because it is an *operator* that takes 3 arguments, rather than a function (which can, of course, take an arbitrarily defined number of arguments).
Common operators like '+' and '*' are `binary` because they take two arguments: arg1 + arg2. There is also `unary`, which is the sign on a signed number (+1 or -1). This is where the ternary nomenclature derives from. The previous posters were confusing functions with operators.
Add comment
|
|
|
|
|
 |
|