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, the if 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.