I l@ve RuBoard Previous Section Next Section

17.6 Simulating the Ternary Operator in Python

Credit: Jürgen Hermann, Alex Martelli, Oliver Steele, Lloyd Goldwasser, Chris Perkins, and Brent Burley

17.6.1 Problem

You want to express in Python the equivalent of C's so-called ternary operator ?: (as in, condition?iftrue:iffalse).

17.6.2 Solution

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))

17.6.3 Discussion

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.

17.6.4 See Also

Recipe 1.10.

    I l@ve RuBoard Previous Section Next Section