5.3. Block structure

Many Python constructions are complex, and can contain statements that have expressions as parts. Consider, for example, the Python if ... then statement:

if verbose > 2:
   print 'Temperature %s' % temperature
else:
   print

This block of code says: If the value of the variable verbose is greater than 2, print out a message, otherwise just print a line break. The part of the statement between the if and the colon (:), is an expression that evaluates to True or False. (verbose > 2). Notice the second line is indented. Many programming languages use such indentation to make the flow of a program visually salient. The if/then statement creates a branching point in the program. On one branch one piece of code is executed. On the other, the other piece. Thus, the indentation helps keep the events on the two branches distinct. Usually the indentation is optional and what really matters is the presence of some kind of bracketing. For example in C++ the above would most likely be printed:

if (verbose > 2){
   print 'Temperature %s' % temperature;}
else
   { print; }

One of the reasons this can be a problem is that different people use different conventions about combining indentation and brackets, making program branches difficult to follow. The unusual feature of Python is that the indentation is obligatory and the brackets are not allowed. That is, the indentation expresses the block structure. This takes some getting used to, and nearly everyone has a bad indentation experience early on, but it is not that hard to pick up, and it is quite effective at making programs more readable. It really helps the transition to use an editor that tries to do the indentation for you. For example the Canopy editor that comes with the free Enthought distribution of Python does this, as does Python mode in the Emacs editor (which is general purpose text editor), but Text Wrangler does not.

There are four basic places where blocks of code are introduced and indentation is obligatory:

  1. Loop structures (for-loops, while loops)

  2. if/then/else statements

  3. Function and class definitions

  4. The Python with construction (used for objects like IO streams with strict initialization and termination procedures).

Here are some examples:

if X > Y:
   print Y

for elem in data:
   print elem[0]
   print elem[1]

def inc_abs_value (x):
   if x >= 0:
     inc = 1
   else:
     inc = -1
   return x + inc

with open('pride_and_prejudice.txt','r') as fh:
    file_contents = fh.read()

Notice the following uniformity: The code blocks are always introduced by a colon (:). Everything in the code block must then be uniformly indented. If a code block occurs inside another code block, as in the definition of inc_abs_value, then the current level of indentation has to be increased. The code block ends when the first unindented line is reached and the unindented line must start at some previously established indentation level, as the return statement does in the definition of inc_abs_value.

Thus, compare the following versions of inc_abs_value:

def inc_abs_value (x):
     if x >= 0:
       inc = 1
     else:
       inc = -1
     return x + inc

def inc_abs_value (x):
     if x >= 0:
       inc = 1
     else:
       inc = -1
       return x + inc

The first version returns a number whether x is positive, 0, or negative. The second version doesn’t return anything when x is non-negative (or, more accurately, it returns None).

As noted above, unindented lines signal the end of a code block. Such line must start at some previously established indentation level. This, the following generates a Python syntax error:

if verbose > 2:
    print 'Temperature %s' % temperature
  print 'Continuing...'
else:
    print

The problem is the line:

print 'Continuing...'

which unindents to an indentation level not previously used before. The error message looks like this:

File "example.py", line 3
  print 'Continuing...'
                           ^
IndentationError: unindent does not match any outer indentation level

The main question to ask yourself when you get an indentation error is “Did I indent where I shouldn’t, or did I miss an indentation where I needed one?” Sometimes, as in the example above, the error message makes it clear which kind of error you made.

The worst kind of indentation errors are the kinds that don’t raise an error message. Suppose that among the many millions of lines of code in your Star Wars Missile Defense system, you meant to include the following:

if bogie_confirmed():

   if alert_level == NORMAL:
      if check_that_bogie_one_more_time():
         fire_missile()
   elif alert_level > NORMAL:
       fire_missile()

This says if the bogie’s confirmed then here’s what we do: If we have a normal alert level, let’s check one more time before firing the missile, but if the alert level is high, why let’s fire that big boy right away! But suppose due to a bad text editor, or a bad hair day, this gets indented a little wrong and we have instead:

if bogie_confirmed():

   if alert_level == NORMAL:
      if check_that_bogie_one_more_time():
         fire_missile()

elif alert_level > NORMAL:
   fire_missile()

Now assume the alert level is above NORMAL. What happens to Western (and Eastern) Civilization when a bogie is confirmed? What about when a bogie is not confirmed?

Here are some more examples from the official Python docs.