Credit: Jürgen Hermann, Alex Martelli, Oliver Steele, Lloyd Goldwasser, Chris Perkins, and Brent Burley
You want to express in Python the
equivalent of C’s so-called ternary operator
?:
(as in,
condition
?
iftrue
:iffalse
).
There are many ways to skin a ternary operator. An explicit
if
/else
is most Pythonic, but
somewhat verbose:
for i in range(1, 3): if i == 1: plural = '' else: plural = 's' print "The loop ran %d time%s" % (i, plural)
Indexing is compact if there are no side effects in the
iftrue
and
iffalse
expressions:
for i in range(1, 3): print "The loop ran %d time%s" % (i, ('', 's')[i != 1])
For the specific case of plurals, there’s also a neat variant using slicing:
for i in range(1, 3): print "The loop ran %d time%s" % (i, "s"[i==1:])
Short-circuited logical expressions can deal correctly with side effects:
for i in range(1, 3): print "The loop ran %d time%s" % (i, i != 1 and 's' or '')
The output of each of these loops is:
The loop ran 1 time The loop ran 2 times
However, the short circuit (which is necessary when either or both of
iftrue
and
iffalse
have side effects) fails if turned
around:
for i in range(1, 3): print "The loop ran %d time%s" % (i, i == 1 and '' or 's')
Since ''
evaluates as false, this snippet outputs:
The loop ran 1 times The loop ran 2 times
So generally, when iftrue
and
iffalse
are unknown at coding time (either
could have side effects or be false), we need:
for i in range(1, 3): print "The loop ran %d time%s" % (i, (i == 1 and [''] or ['s'])[0])
or:
for i in range(1, 3): print "The loop ran %d time%s" % (i, (lambda:'', lambda:'s')[i!=1]( ))
or even weirder variations:
for i in range(1, 3): print "The loop ran %d time%s" % (i, [i==1 and '', i!=1 and 's'][i!=1]) for i in range(1, 3): print "The loop ran %d time%s" % ( i, (i==1 and (lambda:'') or (lambda:'s'))( ))
And now for something completely different (for plurals only, again):
for i in range(1, 3): print "The loop ran %d time%s" % (i, 's'*(i!=1))
Programmers coming to Python from C, C++, or Perl sometimes miss the
so-called ternary operator ?:
. It’s most often
used for avoiding a few lines of code and a temporary variable for
simple decisions, such as printing the plural form of words after a
counter, as in this recipe’s examples. In most
cases, Python’s preference for making things clear
and explicit at the cost of some conciseness is an acceptable
tradeoff, but one can sympathize with the withdrawal symptoms of
ternary-operator addicts.
99.44 times out of 100, you will be better off using a plain
if
/else
statement (perhaps in a
named local function if you wanted an
if
/else
that fits in an
expression to fit that expression inside a lambda
form). But for the remaining 56 cases out of 10,000, the idioms in
this recipe can be useful. A typical case would be if
you’re transliterating from another language into
Python and need to keep program structure as close as possible to the
original, as mentioned in Recipe 1.10.
There are several ways to get the ternary operator effect in Python,
and this recipe tries to display a fair selection of the wide range
of possibilities. One can always, after all, use a good old
if
/else
statement. Indexing can
help, and, for the specific case of plurals, there’s
a neat variant of it based on slicing. However, neither indexing nor
slicing apply to cases in which either or both of the
iftrue
and
iffalse
expressions may have side effects.
If such side effects are an issue, the short-circuiting effect of
and
/or
can be used, but care
may be needed if we don’t know (at coding time) if
iftrue
and
iffalse
have side effects; they might also
be Python values evaluated as false. To meet both the side-effect
issue and the might-be-false risk, two variants in this recipe mix
indexing and function calling or a lambda
form,
but this starts to verge on an excess of subtlety! Just to dispell
any doubt, even weirder mixtures of lambda
and
indexing or short-circuiting are shown at the end of this
recipe.