Embercode
✕
ConditionalsLesson 12 of 14

35 min read

Dangling else and indentation in Python

Why this matters

An if statement can have an else part. The else runs when the test is false. Most of the time this is easy. Sometimes a program has an if inside another if, and then a question appears: which if does this else belong to?

In many languages, such as Java, C and C++, braces { and } mark where each block starts and ends. The rule for which if an else belongs to is then part of the language, and a program can look right on screen while the compiler reads it another way. That mismatch is called the dangling else problem.

Python has no braces. Indentation is the only grouping, so the owner of an else is decided by the spaces at the start of each line. That removes the usual form of the bug, but a wrong indent level causes the same kind of silent mistake. The program runs without an error and prints the wrong thing.

This lesson covers the Python rule, the mistakes that come from wrong indentation, and how to find the owner of any else. It assumes you know if, elif and else from the lesson on if, elif and else in Python.

To run the examples, save each one in a file, such as dangle1.py, and type this in a terminal:

python3 dangle1.py

Indentation is the grouping

A line that ends with a colon, such as if score >= 50:, starts a block. The lines under it, indented by four spaces, form the block. The block ends at the first line that is indented less.

An else is part of the same statement as its if, so it must line up with that if. The rule is this: an else belongs to the nearest if or elif above it at the same indentation level that does not already have an else.

Here is a short program. The else lines up with if vip:, so it belongs to that inner if.

Python · editable

The output is:

Not passed

The score is 75, which is a pass. The program still prints "Not passed", because the else belongs to if vip:, and vip is false. The code says exactly that. The bug is not in the rule, but in what the author meant.

A wrong indent level moves the else

The author of the next program meant the else for the outer if. The score is 30, so the program should print "Not passed". The else is one level too deep, under if score >= 50: instead of under if vip:.

Python · editable

The output is:

end

Nothing from the else prints. The else lines up with if vip:, so it belongs to that if. The outer test is false, so the whole block under if score >= 50: is skipped, and the else inside it never runs. Python gives no warning, because the code is valid.

The fix is to move the else one level to the left, so that it lines up with if score >= 50:.

Python · editable

The output is:

Not passed
end

Two rules come out of this. First, the owner of an else is the nearest if or elif at the same indentation level that has no else yet. Second, indentation is not a hint for the reader. It is the grouping. A wrong level changes the meaning of the program, and Python does not complain.

Without braces, count the levels

With braces, a closing brace after an if body shows where the block ends. Python has no closing brace. The end of a block is the first line that is indented less than the block. So when if statements are nested, every level of indentation must be exact. Use four spaces for each level, every time.

The fix for a dangling else in Python is therefore not to add braces. It is to put each else at the level of the if it belongs to, and to check that the level matches. Count the levels from the left edge of the line. The else and the if should start at the same column.

A shop with an open flag

The next example has an inner if with its own else. The shop is closed today, so the author expects "else part for shop closed" to print. The name is_open is used here, because open is the name of a built-in function in Python. Naming a variable open would hide that function.

Python · editable

The output is:

done

The else is indented under if hour >= 9:, so it belongs to that inner if. The outer test is_open is false, so the whole block is skipped, and the else inside it is skipped with it.

Here is the fixed version. The else lines up with if is_open:, so it belongs to the outer if.

Python · editable

The output is:

else part for shop closed
done

Three levels, each else lined up with its if

With three levels of if, each level can have its own else. Python pairs each else with the if at the same indentation level. Read the program from the top, and find the if that has the same indentation as each else.

Python · editable

The output is:

Open now (weekday)

The else under if hour < 17: belongs to that test. The else under if hour >= 9: belongs to that test. The last else, at the left edge, belongs to if is_open:. Each else lines up with its if, so the owner of each one is visible on the screen.

Finding the owner of an else

When you are not sure which if an else belongs to, apply the rule step by step. Start at the else and move up the code. Skip any line that is indented more deeply than the else, because that line sits inside another block. The first if you reach at the same indentation as the else that has no else yet is the owner.

Use this on the three-level program above. It has three else lines, and each one needs its owner found in turn:

  1. The else before "Closed: after 17:00" is indented 8 spaces. Moving up, the line print("Open now (weekday)") is indented deeper, so it is skipped. The next line up is if hour < 17:, which is also 8 spaces and has no else yet. So it owns this else.
  2. The else before "Closed: before 09:00" is indented 4 spaces. Moving up, the else from step 1, the if hour < 17: line and the lines under them are indented deeper, so they are skipped. The next line at 4 spaces is if hour >= 9:, which has no else yet. So it owns this else.
  3. The else before "Shop is closed today" is at the left edge. Moving up, every line under the if is_open: block is indented deeper, so all of them are skipped. The first line at the left edge is if is_open:, which owns this last else.

Each else used up exactly one if, and the three pairs match the three levels. The walk takes effort every time you read the code. Count the indentation, and the owner of each else is the if at the same column.

elif is the Python form of else if

A chain of tests is written with elif. It is one statement, and the tests are checked from top to bottom. The first test that is true runs its block, and the rest are skipped. There is no nesting, so no else can dangle.

Python · editable

The output is:

medium

Python has no else if form. If you write those two words, Python stops with an error, because the else must be followed by a colon. The error is shown in the mistakes section.

Common mistakes

The examples below come from running real programs. Each mistake shows its real output. The file paths in the error messages depend on where you save the file, so yours will differ.

A line at the wrong indentation level

The author wanted both print lines to run only when a > 0. The second print is at the level of the if line, not under it, so it is not part of the block. Python runs it every time.

Python · editable

The output is:

second, always runs

The first print is skipped, because a is 0. The second print is outside the if, so it runs. To put a line in the block, indent it under the if line. The rule is that a block is every line indented under the if.

A missing colon

Every if, elif and else line ends with a colon. Without it, Python stops before it runs the program.

age = 30
if age < 18
    print("minor")

Python stops with this error:

  File "/path/to/missing_colon.py", line 2
    if age < 18
               ^
SyntaxError: expected ':'

Add the colon at the end of the if line.

An if with no indented body

The print line below is not indented, so it is not part of the if. Python expects a block after the colon, and it finds none.

age = 30
if age < 18:
print("minor")
print("age", age)

Python stops with this error:

  File "/path/to/no_body.py", line 3
    print("minor")
    ^^^^^
IndentationError: expected an indented block after 'if' statement on line 2

Indent the line under the if. If you want an empty block on purpose, write pass on its own indented line. pass does nothing, and it fills the place where a block must be.

A wrong dedent

Moving a line back to a shallower level is a dedent. A line must return to an indentation level that was used before. If the level matches none of them, Python stops.

score = 30
if score >= 50:
    print("pass")
  else:
    print("fail")

Python stops with this error:

  File "/path/to/bad_dedent.py", line 4
    else:
         ^
IndentationError: unindent does not match any outer indentation level

The else has 2 spaces, and no earlier line has that level. Line it up with the if, which has no indentation.

Mixing tabs and spaces

A tab character moves to the next tab stop, and a space moves one column. A block indented with a tab on one line and with spaces on the next can line up on the screen and still have different meanings to Python. Python 3 refuses to run such a file. The last line of the error message is this:

TabError: inconsistent use of tabs and spaces in indentation

Use spaces only. Most editors can be set to insert four spaces when you press the Tab key.

An else with no if

If a second else appears after an else that already has an owner, there is no if left for it. Python stops with a syntax error.

level = 2
if level > 1:
    print("above one")
else:
    print("one or less")
else:
    print("never")

Python stops with this error:

  File "/path/to/extra_else.py", line 6
    else:
    ^^^^
SyntaxError: invalid syntax

The message points at the second else on line 6. The first else already belongs to the if on line 2. The second else has nothing to attach to. The fix is to delete the second else block, or to write the second test as elif with its own condition, depending on what you meant.

Writing else if

The words else if look like the word elif, but Python reads them as an else followed by an if on the same line. The else needs a colon right after it, so Python stops at the word if.

x = 3
if x > 10:
    print("big")
else if x > 2:
    print("medium")

Python stops with this error:

  File "/path/to/else_if.py", line 4
    else if x > 2:
         ^^
SyntaxError: expected ':'

Write elif x > 2: instead.

Fixing the indentation but not the code

Some programs look confusing, and a formatting tool is run on them to make them tidy. A formatter changes the spaces at the start of each line, but it keeps the grouping that the indentation already shows. It cannot tell what you meant. If an else is under the wrong if, the formatter leaves it there. The only fix is to move the line to the right level, by hand, and then check the result against the rule in this lesson.

Practice

Each exercise has its answer below it. Try to work it out before you read the answer, and run the code if you can.

Exercise 1: What does this print?

n = 5
if n > 10:
    if n > 3:
        print("A")
    else:
        print("B")
print("C")

Show the answer

The output is C, and nothing else. The else belongs to if n > 3:. The outer test n > 10 is false, so the whole inner block is skipped, including its else. Only the last print runs.

Exercise 2: Fix the indentation so that "Low" prints

The code below was meant to print "Low" when temp is less than 10. It prints nothing.

temp = 5
raining = False
 
if temp >= 10:
    if raining:
        print("Warm and wet")
    else:
        print("Low")

Show the answer

The else belongs to if raining:, but it should belong to if temp >= 10:. Move it one level to the left:

temp = 5
raining = False
 
if temp >= 10:
    if raining:
        print("Warm and wet")
else:
    print("Low")

The program prints Low. With temp set to 5, the outer test is false, so the else runs.

Exercise 3: Why does this program not run?

age = 30
if age < 18:
print("minor")
print("age", age)

Show the answer

Python stops with IndentationError: expected an indented block after 'if' statement on line 2. The print("minor") line must be indented under the if. With the indentation added, the program prints age 30, and the minor line never runs, because 30 is not less than 18.

Exercise 4: Classify each number

Write a program that loops over the values 4, -3, 7 and 0. For each value, print x is positive if it is greater than zero, followed by x is positive even or x is positive odd. For values that are not positive, print x is not positive.

Show the answer

Python · editable

The output is:

4 is positive
4 is positive even
-3 is not positive
7 is positive
7 is positive odd
0 is not positive

The inner if is indented under the outer if x > 0:, so its else belongs to if x % 2 == 0:. The outer else lines up with if x > 0:, so it belongs to that test.

Exercise 5: Read the error

The code below does not run. Explain the error message and fix the code.

level = 2
if level > 1:
    print("above one")
else:
    print("one or less")
else:
    print("never")

Show the answer

Python reports SyntaxError: invalid syntax at the second else. The first else is valid and belongs to if level > 1:. The second else has no if left to attach to, so Python stops. The fix is to delete the second else block, since the first else already covers the other case. If you meant a different test, write it as elif with its own condition.

Exercise 6: Which if owns each else?

Run this code with a = 2, then change a to 0 and run it again. What does each run print?

a = 2
b = 9
if a > 1:
    if b > 5:
        print("x")
    else:
        print("y")
else:
    print("z")

Show the answer

With a = 2, the outer test is true. The inner test b > 5 is also true, so x prints. The else under if b > 5: is skipped. The last else lines up with if a > 1:, so it is skipped too. The output is x.

With a = 0, the outer test is false. The whole block under if a > 1: is skipped, so the else at the left edge runs. The output is z. The inner if never gets a turn, and its own else prints nothing.

Summary

  • An else belongs to the nearest if or elif above it at the same indentation level that does not already have an else.
  • Python uses indentation as the grouping. A wrong indent level changes the meaning of the program, and Python does not warn about it.
  • Count the levels from the left edge. The else and its if should start at the same column.
  • Use elif for a chain of tests. Python has no else if form.
  • An else with no if to attach to is a syntax error, and the message is SyntaxError: invalid syntax.
Practice this topic: coding problems for Conditionals →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Decision tables in Python