Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 19 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# explicit if
for i in range(1,3):
    if i == 1:
        plural = ''
    else:
        plural = 's'
    print "The loop ran %d time%s" % (i, plural)

# selecting from tuple
for i in range(1,3):
    print "The loop ran %d time%s" % (i, ('','s')[i != 1])

# short-circuited logical expression
for i in range(1,3):
    print "The loop ran %d time%s" % (i, i != 1 and 's' or '')

# Output of all loops:
# The loop ran 1 time
# The loop ran 2 times

15 comments

Hamish Lawson 20 years, 8 months ago  # | flag

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). Hamish Lawson

Michael Chermside 20 years, 8 months ago  # | flag

Important note and another approach. 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. <P> 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). <p> -- Michael Chermside Michael Chermside

Hamish Lawson 20 years, 8 months ago  # | flag

Accomodating false val_1 while keeping shortcircuit behaviour. 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. Hamish Lawson

Brent Burley 20 years, 8 months ago  # | flag

One more variation. 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))

Brent Burley

Lloyd Goldwasser 20 years, 5 months ago  # | flag

simulating the ternary operator. The Algorithm category contains an entry by Alex Martelli on exactly this topic, with at least two solutions not included here...

Stefan Schwarzer 19 years, 10 months ago  # | flag

a? b : c emulation is Python FAQ 4.16. 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.

Philip Kromer 19 years, 5 months ago  # | flag

Can also use the overloaded multiplication operator for strings. You can also do

's'*(i!=1)

which is as close to obfuscated python as we can get.

Clark Updike 17 years ago  # | flag

iif only you had chosen a better name ;-). 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 :-)

Martin Freedman 16 years, 8 months ago  # | flag

a bound lambda solution. 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.

Martin Freedman 16 years, 8 months ago  # | flag

update. 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!

Martin Freedman 16 years, 8 months ago  # | flag

update to update. If only I could edit my comments :-( - You still to lambda b or c if fucntions to avoid side effects.

Christopher Dunn 16 years, 7 months ago  # | flag

A more flexible version.

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)
Rick Graves 16 years, 5 months ago  # | flag

better ternary operator? 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

Rick Graves 16 years, 5 months ago  # | flag

more about ternary operator. 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.

Jason Yamada-Hanff 14 years, 2 months ago  # | flag

ternary operator and 2.5. 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.