Learning Python (2013)

Part III. Statements and Syntax

Chapter 12. if Tests and Syntax Rules

This chapter presents the Python if statement, which is the main statement used for selecting from alternative actions based on test results. Because this is our first in-depth look at compound statements—statements that embed other statements—we will also explore the general concepts behind the Python statement syntax model here in more detail than we did in the introduction in Chapter 10. Because the if statement introduces the notion of tests, this chapter will also deal with Boolean expressions, cover the “ternary” if expression, and fill in some details on truth tests in general.

if Statements

In simple terms, the Python if statement selects actions to perform. Along with its expression counterpart, it’s the primary selection tool in Python and represents much of the logic a Python program possesses. It’s also our first compound statement. Like all compound Python statements, theif statement may contain other statements, including other ifs. In fact, Python lets you combine statements in a program sequentially (so that they execute one after another), and in an arbitrarily nested fashion (so that they execute only under certain conditions such as selections and loops).

General Format

The Python if statement is typical of if statements in most procedural languages. It takes the form of an if test, followed by one or more optional elif (“else if”) tests and a final optional else block. The tests and the else part each have an associated block of nested statements, indented under a header line. When the if statement runs, Python executes the block of code associated with the first test that evaluates to true, or the else block if all tests prove false. The general form of an if statement looks like this:

if test1:                 # if test

    statements1           # Associated block

elif test2:               # Optional elifs

    statements2

else:                     # Optional else

    statements3

Basic Examples

To demonstrate, let’s look at a few simple examples of the if statement at work. All parts are optional, except the initial if test and its associated statements. Thus, in the simplest case, the other parts are omitted:

>>> if 1:

...     print('true')

...

true

Notice how the prompt changes to ... for continuation lines when you’re typing interactively in the basic interface used here; in IDLE, you’ll simply drop down to an indented line instead (hit Backspace to back up). A blank line (which you can get by pressing Enter twice) terminates and runs the entire statement. Remember that 1 is Boolean true (as we’ll see later, the word True is its equivalent), so this statement’s test always succeeds. To handle a false result, code the else:

>>> if not 1:

...     print('true')

... else:

...     print('false')

...

false

Multiway Branching

Now here’s an example of a more complex if statement, with all its optional parts present:

>>> x = 'killer rabbit'

>>> if x == 'roger':

...     print("shave and a haircut")

... elif x == 'bugs':

...     print("what's up doc?")

... else:

...     print('Run away! Run away!')

...

Run away! Run away!

This multiline statement extends from the if line through the block nested under the else. When it’s run, Python executes the statements nested under the first test that is true, or the else part if all tests are false (in this example, they are). In practice, both the elif and else parts may be omitted, and there may be more than one statement nested in each section. Note that the words if, elif, and else are associated by the fact that they line up vertically, with the same indentation.

If you’ve used languages like C or Pascal, you might be interested to know that there is no switch or case statement in Python that selects an action based on a variable’s value. Instead, you usually code multiway branching as a series of if/elif tests, as in the prior example, and occasionally by indexing dictionaries or searching lists. Because dictionaries and lists can be built at runtime dynamically, they are sometimes more flexible than hardcoded if logic in your script:

>>> choice = 'ham'

>>> print({'spam':  1.25,         # A dictionary-based 'switch'

...        'ham':   1.99,         # Use has_key or get for default

...        'eggs':  0.99,

...        'bacon': 1.10}[choice])

1.99

Although it may take a few moments for this to sink in the first time you see it, this dictionary is a multiway branch—indexing on the key choice branches to one of a set of values, much like a switch in C. An almost equivalent but more verbose Python if statement might look like the following:

>>> if choice == 'spam':          # The equivalent if statement

...     print(1.25)

... elif choice == 'ham':

...     print(1.99)

... elif choice == 'eggs':

...     print(0.99)

... elif choice == 'bacon':

...     print(1.10)

... else:

...     print('Bad choice')

...

1.99

Though it’s perhaps more readable, the potential downside of an if like this is that, short of constructing it as a string and running it with tools like the prior chapter’s eval or exec, you cannot construct it at runtime as easily as a dictionary. In more dynamic programs, data structures offer added flexibility.

Handling switch defaults

Notice the else clause on the if here to handle the default case when no key matches. As we saw in Chapter 8, dictionary defaults can be coded with in expressions, get method calls, or exception catching with the try statement introduced in the preceding chapter. All of the same techniques can be used here to code a default action in a dictionary-based multiway branch. As a review in the context of this use case, here’s the get scheme at work with defaults:

>>> branch = {'spam': 1.25,

...           'ham':  1.99,

...           'eggs': 0.99}

>>> print(branch.get('spam', 'Bad choice'))

1.25

>>> print(branch.get('bacon', 'Bad choice'))

Bad choice

An in membership test in an if statement can have the same default effect:

>>> choice = 'bacon'

>>> if choice in branch:

...     print(branch[choice])

... else:

...     print('Bad choice')

...

Bad choice

And the try statement is a general way to handle defaults by catching and handling the exceptions they’d otherwise trigger (for more on exceptions, see Chapter 11’s overview and Part VII’s full treatment):

>>> try:

...     print(branch[choice])

... except KeyError:

...     print('Bad choice')

...

Bad choice

Handling larger actions

Dictionaries are good for associating values with keys, but what about the more complicated actions you can code in the statement blocks associated with if statements? In Part IV, you’ll learn that dictionaries can also contain functions to represent more complex branch actions and implement general jump tables. Such functions appear as dictionary values, they may be coded as function names or inline lambdas, and they are called by adding parentheses to trigger their actions. Here’s an abstract sampler, but stay tuned for a rehash of this topic in Chapter 19 after we’ve learned more about function definition:

def function(): ...

def default(): ...

branch = {'spam': lambda: ...,             # A table of callable function objects

          'ham':  function,

          'eggs': lambda: ...}

branch.get(choice, default)()

Although dictionary-based multiway branching is useful in programs that deal with more dynamic data, most programmers will probably find that coding an if statement is the most straightforward way to perform multiway branching. As a rule of thumb in coding, when in doubt, err on the side of simplicity and readability; it’s the “Pythonic” way.

Python Syntax Revisited

I introduced Python’s syntax model in Chapter 10. Now that we’re stepping up to larger statements like if, this section reviews and expands on the syntax ideas introduced earlier. In general, Python has a simple, statement-based syntax. However, there are a few properties you need to knowabout:

§  Statements execute one after another, until you say otherwise. Python normally runs statements in a file or nested block in order from first to last as a sequence, but statements like if (as well as loops and exceptions) cause the interpreter to jump around in your code. Because Python’s path through a program is called the control flow, statements such as if that affect it are often called control-flow statements.

§  Block and statement boundaries are detected automatically. As we’ve seen, there are no braces or “begin/end” delimiters around blocks of code in Python; instead, Python uses the indentation of statements under a header to group the statements in a nested block. Similarly, Python statements are not normally terminated with semicolons; rather, the end of a line usually marks the end of the statement coded on that line. As a special case, statements can span lines and be combined on a line with special syntax.

§  Compound statements = header + “:” + indented statements. All Python compound statements—those with nested statements—follow the same pattern: a header line terminated with a colon, followed by one or more nested statements, usually indented under the header. The indented statements are called a block (or sometimes, a suite). In the if statement, the elif and else clauses are part of the if, but they are also header lines with nested blocks of their own. As a special case, blocks can show up on the same line as the header if they are simple noncompound code.

§  Blank lines, spaces, and comments are usually ignored. Blank lines are both optional and ignored in files (but not at the interactive prompt, when they terminate compound statements). Spaces inside statements and expressions are almost always ignored (except in string literals, and when used for indentation). Comments are always ignored: they start with a # character (not inside a string literal) and extend to the end of the current line.

§  Docstrings are ignored but are saved and displayed by tools. Python supports an additional comment form called documentation strings (docstrings for short), which, unlike # comments, are retained at runtime for inspection. Docstrings are simply strings that show up at the top of program files and some statements. Python ignores their contents, but they are automatically attached to objects at runtime and may be displayed with documentation tools like PyDoc. Docstrings are part of Python’s larger documentation strategy and are covered in the last chapter in this part of the book.

As you’ve seen, there are no variable type declarations in Python; this fact alone makes for a much simpler language syntax than what you may be used to. However, for most new users the lack of the braces and semicolons used to mark blocks and statements in many other languages seems to be the most novel syntactic feature of Python, so let’s explore what this means in more detail.

Block Delimiters: Indentation Rules

As introduced in Chapter 10, Python detects block boundaries automatically, by line indentation—that is, the empty space to the left of your code. All statements indented the same distance to the right belong to the same block of code. In other words, the statements within a block line up vertically, as in a column. The block ends when the end of the file or a lesser-indented line is encountered, and more deeply nested blocks are simply indented further to the right than the statements in the enclosing block. Compound statement bodies can appear on the header’s line in some cases we’ll explore later, but most are indented under it.

For instance, Figure 12-1 demonstrates the block structure of the following code:

x = 1

if x:

    y = 2

    if y:

        print('block2')

    print('block1')

print('block0')

Nested blocks of code: a nested block starts with a statement indented further to the right and ends with either a statement that is indented less, or the end of the file.

Figure 12-1. Nested blocks of code: a nested block starts with a statement indented further to the right and ends with either a statement that is indented less, or the end of the file.

This code contains three blocks: the first (the top-level code of the file) is not indented at all, the second (within the outer if statement) is indented four spaces, and the third (the print statement under the nested if) is indented eight spaces.

In general, top-level (unnested) code must start in column 1. Nested blocks can start in any column; indentation may consist of any number of spaces and tabs, as long as it’s the same for all the statements in a given single block. That is, Python doesn’t care how you indent your code; it only cares that it’s done consistently. Four spaces or one tab per indentation level are common conventions, but there is no absolute standard in the Python world.

Indenting code is quite natural in practice. For example, the following (arguably silly) code snippet demonstrates common indentation errors in Python code:

  x = 'SPAM'                        # Error: first line indented

if 'rubbery' in 'shrubbery':

    print(x * 8)

        x += 'NI'                   # Error: unexpected indentation

        if x.endswith('NI'):

                x *= 2

            print(x)                # Error: inconsistent indentation

The properly indented version of this code looks like the following—even for an artificial example like this, proper indentation makes the code’s intent much more apparent:

x = 'SPAM'

if 'rubbery' in 'shrubbery':

    print(x * 8)                    # Prints 8 "SPAM"

    x += 'NI'

    if x.endswith('NI'):

        x *= 2

        print(x)                    # Prints "SPAMNISPAMNI"

It’s important to know that the only major place in Python where whitespace matters is where it’s used to the left of your code, for indentation; in most other contexts, space can be coded or not. However, indentation is really part of Python syntax, not just a stylistic suggestion: all the statements within any given single block must be indented to the same level, or Python reports a syntax error. This is intentional—because you don’t need to explicitly mark the start and end of a nested block of code, some of the syntactic clutter found in other languages is unnecessary in Python.

As described in Chapter 10, making indentation part of the syntax model also enforces consistency, a crucial component of readability in structured programming languages like Python. Python’s syntax is sometimes described as “what you see is what you get”—the indentation of each line of code unambiguously tells readers what it is associated with. This uniform and consistent appearance makes Python code easier to maintain and reuse.

Indentation is simpler in practice than its details might initially imply, and it makes your code reflect its logical structure. Consistently indented code always satisfies Python’s rules. Moreover, most text editors (including IDLE) make it easy to follow Python’s indentation model by automatically indenting code as you type it.

Avoid mixing tabs and spaces: New error checking in 3.X

One rule of thumb: although you can use spaces or tabs to indent, it’s usually not a good idea to mix the two within a block—use one or the other. Technically, tabs count for enough spaces to move the current column number up to a multiple of 8, and your code will work if you mix tabs and spaces consistently. However, such code can be difficult to change. Worse, mixing tabs and spaces makes your code difficult to read completely apart from Python’s syntax rules—tabs may look very different in the next programmer’s editor than they do in yours.

In fact, Python 3.X issues an error, for these very reasons, when a script mixes tabs and spaces for indentation inconsistently within a block (that is, in a way that makes it dependent on a tab’s equivalent in spaces). Python 2.X allows such scripts to run, but it has a -t command-line flag that will warn you about inconsistent tab usage and a -tt flag that will issue errors for such code (you can use these switches in a command line like python –t main.py in a system shell window). Python 3.X’s error case is equivalent to 2.X’s -tt switch.

Statement Delimiters: Lines and Continuations

A statement in Python normally ends at the end of the line on which it appears. When a statement is too long to fit on a single line, though, a few special rules may be used to make it span multiple lines:

§  Statements may span multiple lines if you’re continuing an open syntactic pair. Python lets you continue typing a statement on the next line if you’re coding something enclosed in a (), {}, or [] pair. For instance, expressions in parentheses and dictionary and list literals can span any number of lines; your statement doesn’t end until the Python interpreter reaches the line on which you type the closing part of the pair (a ), }, or ]). Continuation lines—lines 2 and beyond of the statement—can start at any indentation level you like, but you should try to make them align vertically for readability if possible. This open pairs rule also covers set and dictionary comprehensions in Python 3.X and 2.7.

§  Statements may span multiple lines if they end in a backslash. This is a somewhat outdated feature that’s not generally recommended, but if a statement needs to span multiple lines, you can also add a backslash (a \ not embedded in a string literal or comment) at the end of the prior line to indicate you’re continuing on the next line. Because you can also continue by adding parentheses around most constructs, backslashes are rarely used today. This approach is also error-prone: accidentally forgetting a \ usually generates a syntax error and might even cause the next line to be silently mistaken (i.e., without warning) for a new statement, with unexpected results.

§  Special rules for string literals. As we learned in Chapter 7, triple-quoted string blocks are designed to span multiple lines normally. We also learned in Chapter 7 that adjacent string literals are implicitly concatenated; when it’s used in conjunction with the open pairs rule mentioned earlier, wrapping this construct in parentheses allows it to span multiple lines.

§  Other rules. There are a few other points to mention with regard to statement delimiters. Although it is uncommon, you can terminate a statement with a semicolon—this convention is sometimes used to squeeze more than one simple (noncompound) statement onto a single line. Also, comments and blank lines can appear anywhere in a file; comments (which begin with a # character) terminate at the end of the line on which they appear.

A Few Special Cases

Here’s what a continuation line looks like using the open syntactic pairs rule just described. Delimited constructs, such as lists in square brackets, can span across any number of lines:

L = ["Good",

     "Bad",

     "Ugly"]                     # Open pairs may span lines

This also works for anything in parentheses (expressions, function arguments, function headers, tuples, and generator expressions), as well as anything in curly braces (dictionaries and, in 3.X and 2.7, set literals and set and dictionary comprehensions). Some of these are tools we’ll study in later chapters, but this rule naturally covers most constructs that span lines in practice.

If you like using backslashes to continue lines, you can, but it’s not common practice in Python:

if a == b and c == d and   \

   d == e and f == g:

   print('olde')                 # Backslashes allow continuations...

Because any expression can be enclosed in parentheses, you can usually use the open pairs technique instead if you need your code to span multiple lines—simply wrap a part of your statement in parentheses:

if (a == b and c == d and

    d == e and e == f):

    print('new')                 # But parentheses usually do too, and are obvious

In fact, backslashes are generally frowned on by most Python developers, because they’re too easy to not notice and too easy to omit altogether. In the following, x is assigned 10 with the backslash, as intended; if the backslash is accidentally omitted, though, x is assigned 6 instead, and no error is reported (the +4 is a valid expression statement by itself).

In a real program with a more complex assignment, this could be the source of a very nasty bug:[28]

x = 1 + 2 + 3 \                  # Omitting the \ makes this very different!

+4

As another special case, Python allows you to write more than one noncompound statement (i.e., statements without nested statements) on the same line, separated by semicolons. Some coders use this form to save program file real estate, but it usually makes for more readable code if you stick to one statement per line for most of your work:

x = 1; y = 2; print(x)           # More than one simple statement

As we learned in Chapter 7, triple-quoted string literals span lines too. In addition, if two string literals appear next to each other, they are concatenated as if a + had been added between them—when used in conjunction with the open pairs rule, wrapping in parentheses allows this form to span multiple lines. For example, the first of the following inserts newline characters at line breaks and assigns S to '\naaaa\nbbbb\ncccc', and the second implicitly concatenates and assigns S to 'aaaabbbbcccc'; as we also saw in Chapter 7, # comments are ignored in the second form, but included in the string in the first:

S = """

aaaa

bbbb

cccc"""

S = ('aaaa'

     'bbbb'                      # Comments here are ignored

     'cccc')

Finally, Python lets you move a compound statement’s body up to the header line, provided the body contains just simple (noncompound) statements. You’ll most often see this used for simple if statements with a single test and action, as in the interactive loops we coded in Chapter 10:

if 1: print('hello')             # Simple statement on header line

You can combine some of these special cases to write code that is difficult to read, but I don’t recommend it; as a rule of thumb, try to keep each statement on a line of its own, and indent all but the simplest of blocks. Six months down the road, you’ll be happy you did.


[28] Candidly, it was a bit surprising that backslash continuations were not removed in Python 3.0, given the broad scope of its other changes! See the 3.0 changes tables in Appendix C for a list of 3.0 removals; some seem fairly innocuous in comparison with the dangers inherent in backslash continuations. Then again, this book’s goal is Python instruction, not populist outrage, so the best advice I can give is simply: don’t do this. You should generally avoid backslash continuations in new Python code, even if you developed the habit in your C programming days.

Truth Values and Boolean Tests

The notions of comparison, equality, and truth values were introduced in Chapter 9. Because the if statement is the first statement we’ve looked at that actually uses test results, we’ll expand on some of these ideas here. In particular, Python’s Boolean operators are a bit different from their counterparts in languages like C. In Python:

§  All objects have an inherent Boolean true or false value.

§  Any nonzero number or nonempty object is true.

§  Zero numbers, empty objects, and the special object None are considered false.

§  Comparisons and equality tests are applied recursively to data structures.

§  Comparisons and equality tests return True or False (custom versions of 1 and 0).

§  Boolean and and or operators return a true or false operand object.

§  Boolean operators stop evaluating (“short circuit”) as soon as a result is known.

The if statement takes action on truth values, but Boolean operators are used to combine the results of other tests in richer ways to produce new truth values. More formally, there are three Boolean expression operators in Python:

X and Y

Is true if both X and Y are true

X or Y

Is true if either X or Y is true

not X

Is true if X is false (the expression returns True or False)

Here, X and Y may be any truth value, or any expression that returns a truth value (e.g., an equality test, range comparison, and so on). Boolean operators are typed out as words in Python (instead of C’s &&, ||, and !). Also, Boolean and and or operators return a true or false object in Python, not the values True or False. Let’s look at a few examples to see how this works:

>>> 2 < 3, 3 < 2        # Less than: return True or False (1 or 0)

(True, False)

Magnitude comparisons such as these return True or False as their truth results, which, as we learned in Chapter 5 and Chapter 9, are really just custom versions of the integers 1 and 0 (they print themselves differently but are otherwise the same).

On the other hand, the and and or operators always return an object—either the object on the left side of the operator or the object on the right. If we test their results in if or other statements, they will be as expected (remember, every object is inherently true or false), but we won’t get back a simple True or False.

For or tests, Python evaluates the operand objects from left to right and returns the first one that is true. Moreover, Python stops at the first true operand it finds. This is usually called short-circuit evaluation, as determining a result short-circuits (terminates) the rest of the expression as soon as the result is known:

>>> 2 or 3, 3 or 2      # Return left operand if true

(2, 3)                  # Else, return right operand (true or false)

>>> [] or 3

3

>>> [] or {}

{}

In the first line of the preceding example, both operands (2 and 3) are true (i.e., are nonzero), so Python always stops and returns the one on the left—it determines the result because true or anything is always true. In the other two tests, the left operand is false (an empty object), so Python simply evaluates and returns the object on the right—which may happen to have either a true or a false value when tested.

Python and operations also stop as soon as the result is known; however, in this case Python evaluates the operands from left to right and stops if the left operand is a false object because it determines the result—false and anything is always false:

>>> 2 and 3, 3 and 2    # Return left operand if false

(3, 2)                  # Else, return right operand (true or false)

>>> [] and {}

[]

>>> 3 and []

[]

Here, both operands are true in the first line, so Python evaluates both sides and returns the object on the right. In the second test, the left operand is false ([]), so Python stops and returns it as the test result. In the last test, the left side is true (3), so Python evaluates and returns the object on the right—which happens to be a false [].

The end result of all this is the same as in C and most other languages—you get a value that is logically true or false if tested in an if or while according to the normal definitions of or and and. However, in Python Booleans return either the left or the right object, not a simple integer flag.

This behavior of and and or may seem esoteric at first glance, but see this chapter’s sidebar Why You Will Care: Booleans for examples of how it is sometimes used to advantage in coding by Python programmers. The next section also shows a common way to leverage this behavior, and its more mnemonic replacement in recent versions of Python.

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

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), 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, running just Y or Z but not both. Here are some examples of it in action:

>>> A = 't' if 'spam' else 'f'      # For strings, nonempty means 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 as the preceding section described:

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 X if false the and skips Y, and the or simply returns Z. In other words, we get “if X then Y else Z.” This is equivalent to the ternary form:

A = Y if X else Z

The and/or combination form 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 if/else expression when you need this structure, 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 as offsets 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 Python code written by ex–C programmers who haven’t quite let go of their dark coding pasts).[29]

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

assigns 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 the short-circuit evaluation of Boolean operators and the if/else, because it may prevent actions from running. Expressions on the right of a Boolean operator, for example, 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 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 Boolean values True and False are the same as the integers1 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 related discussion in 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.7). 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.

Finally, and as a preview, other tools in Python have roles similar to the or chains at the start of this sidebar: the filter call and list comprehensions we’ll meet later can be used to select true values when the set of candidates isn’t known until runtime (though they evaluate all values and return all that are true), and the any and all built-ins can be used to test if any or all items in a collection are true (though they don’t select an item):

>>> L = [1, 0, 2, 0, 'spam', '', 'ham', []]

>>> list(filter(bool, L))                    # Get true values

[1, 2, 'spam', 'ham']

>>> [x for x in L if x]                      # Comprehensions

[1, 2, 'spam', 'ham']

>>> any(L), all(L)                           # Aggregate truth

(True, False)

As seen in Chapter 9, the bool function here simply returns its argument’s true or false value, as though it were tested in an if. Watch for more on these related tools in Chapter 14Chapter 19, and Chapter 20.


[29] In fact, Python’s Y if X else Z has a slightly different order than C’s X ? Y : Z, and uses more readable words. Its differing order was reportedly chosen in response to analysis of common usage patterns in Python code. According to the Python folklore, 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. If you have to work at packing logic into expressions like this, statements are probably your better bet.

Chapter Summary

In this chapter, we studied the Python if statement. Additionally, because this was our first compound and logical statement, we reviewed Python’s general syntax rules and explored the operation of truth values and tests in more depth than we were able to previously. Along the way, we also looked at how to code multiway branching in Python, learned about the if/else expression introduced in Python 2.5, and explored some common ways that Boolean values crop up in code.

The next chapter continues our look at procedural statements by expanding on the while and for loops. There, we’ll learn about alternative ways to code loops in Python, some of which may be better than others. Before that, though, here is the usual chapter quiz.

Test Your Knowledge: Quiz

1.    How might you code a multiway branch in Python?

2.    How can you code an if/else statement as an expression in Python?

3.    How can you make a single statement span many lines?

4.    What do the words True and False mean?

Test Your Knowledge: Answers

1.    An if statement with multiple elif clauses is often the most straightforward way to code a multiway branch, though not necessarily the most concise or flexible. Dictionary indexing can often achieve the same result, especially if the dictionary contains callable functions coded withdef statements or lambda expressions.

2.    In Python 2.5 and later, the expression form Y if X else Z returns Y if X is true, or Z otherwise; it’s the same as a four-line if statement. The and/or combination (((X and Y) or Z)) can work the same way, but it’s more obscure and requires that the Y part be true.

3.    Wrap up the statement in an open syntactic pair ((), [], or {}), and it can span as many lines as you like; the statement ends when Python sees the closing (right) half of the pair, and lines 2 and beyond of the statement can begin at any indentation level. Backslash continuations work too, but are broadly discouraged in the Python world.

4.    True and False are just custom versions of the integers 1 and 0, respectively: they always stand for Boolean true and false values in Python. They’re available for use in truth tests and variable initialization, and are printed for expression results at the interactive prompt. In all these roles, they serve as a more mnemonic and hence readable alternative to 1 and 0.