The if/else Ternary Expression

One common role for the prior section’s Boolean operators is to code an expression that runs the same as an if statement. Consider the following statement, which sets A to either Y or Z, based on the truth value of X:

if X:
    A = Y
else:
    A = Z

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

Sometimes, though, the items involved in such a statement are so simple that it seems like overkill to spread them across four lines. At other times, we may want to nest such a construct in a larger statement instead of assigning its result to a variable. For these reasons (and, frankly, because the C language has a similar tool[32]), Python 2.5 introduced a new expression format that allows us to say the same thing in one expression:

A = Y if X else Z

This expression has the exact same effect as the preceding four-line if statement, but it’s simpler to code. As in the statement equivalent, Python runs expression Y only if X turns out to be true, and runs expression Z only if X turns out to be false. That is, it short-circuits, just like the Boolean operators described in the prior section. Here are some examples of it in action:

>>> A = 't' if 'spam' else 'f'      # Nonempty is true
>>> A
't'
>>> A = 't' if '' else 'f'
>>> A
'f'

Prior to Python 2.5 (and after 2.5, if you insist), the same effect can often be achieved by a careful combination of the and and or operators, because they return either the object on the left side or the object on the right:

A = ((X and Y) or Z)

This works, but there is a catch—you have to be able to assume that Y will be Boolean true. If that is the case, the effect is the same: the and runs first and returns Y if X is true; if it’s not, the or simply returns Z. In other words, we get “if X then Y else Z.”

This and/or combination also seems to require a “moment of great clarity” to understand the first time you see it, and it’s no longer required as of 2.5—use the equivalent and more robust and mnemonic Y if X else Z instead if you need this as an expression, or use a full if statement if the parts are nontrivial.

As a side note, using the following expression in Python is similar because the bool function will translate X into the equivalent of integer 1 or 0, which can then be used to pick true and false values from a list:

A = [Z, Y][bool(X)]

For example:

>>> ['f', 't'][bool('')]
'f'
>>> ['f', 't'][bool('spam')]
't'

However, this isn’t exactly the same, because Python will not short-circuit—it will always run both Z and Y, regardless of the value of X. Because of such complexities, you’re better off using the simpler and more easily understood if/else expression as of Python 2.5 and later. Again, though, you should use even that sparingly, and only if its parts are all fairly simple; otherwise, you’re better off coding the full if statement form to make changes easier in the future. Your coworkers will be happy you did.

Still, you may see the and/or version in code written prior to 2.5 (and in code written by C programmers who haven’t quite let go of their dark coding pasts...).


Why You Will Care: Booleans

One common way to use the somewhat unusual behavior of Python Boolean operators is to select from a set of objects with an or. A statement such as this:

X = A or B or C or None

sets X to the first nonempty (that is, true) object among A, B, and C, or to None if all of them are empty. This works because the or operator returns one of its two objects, and it turns out to be a fairly common coding paradigm in Python: to select a nonempty object from among a fixed-size set, simply string them together in an or expression. In simpler form, this is also commonly used to designate a default—the following sets X to A if A is true (or nonempty), and to default otherwise:

X = A or default

It’s also important to understand short-circuit evaluation because expressions on the right of a Boolean operator might call functions that perform substantial or important work, or have side effects that won’t happen if the short-circuit rule takes effect:

if f1() or f2(): ...

Here, if f1 returns a true (or nonempty) value, Python will never run f2. To guarantee that both functions will be run, call them before the or:

tmp1, tmp2 = f1(), f2()
if tmp1 or tmp2: ...

You’ve already seen another application of this behavior in this chapter: because of the way Booleans work, the expression ((A and B) or C) can be used to emulate an if/else statement—almost (see this chapter’s discussion of this form for details).

We met additional Boolean use cases in prior chapters. As we saw in Chapter 9, because all objects are inherently true or false, it’s common and easier in Python to test an object directly (if X:) than to compare it to an empty value (if X != '':). For a string, the two tests are equivalent. As we also saw in Chapter 5, the preset Booleans values True and False are the same as the integers 1 and 0 and are useful for initializing variables (X = False), for loop tests (while True:), and for displaying results at the interactive prompt.

Also watch for the discussion of operator overloading in Part VI: when we define new object types with classes, we can specify their Boolean nature with either the __bool__ or __len__ methods (__bool__ is named __nonzero__ in 2.6). The latter of these is tried if the former is absent and designates false by returning a length of zero—an empty object is considered false.


 


[32] In fact, Python’s X if Y else Z has a slightly different order than C’s Y ? X : Z. This was reportedly done in response to analysis of common use patterns in Python code. According to rumor, this order was also chosen in part to discourage ex-C programmers from overusing it! Remember, simple is better than complex, in Python and elsewhere.