Control flow · about 40 min

Making decisions: if, elif and else

Why this matters

So far, every program has run every line, top to bottom, the same way for every input. Real programs make decisions: reject invalid input, charge shipping only under a certain order total, print an error if a file is missing. The if statement is how a program chooses between paths based on data. It is also where you meet Python's most distinctive feature: indentation defines structure.

The if statement

temperature = int(input())
if temperature > 30:
    print("It's hot.")
    print("Drink water.")
print("Have a nice day.")

With input 35:

It's hot.
Drink water.
Have a nice day.

With input 20:

Have a nice day.

The anatomy:

Indentation is syntax

In many languages, braces { } mark blocks and indentation is just a courtesy to readers. In Python, the indentation is the block marker. The tokenizer (remember the compile pipeline) turns an increase in indentation into an INDENT token and a decrease into a DEDENT token, and the grammar uses those exactly as other languages use braces.

if temperature > 30:        <- header, ends with :
    print("It's hot.")      <- INDENT: block starts
    print("Drink water.")   <- same level: same block
print("Have a nice day.")   <- DEDENT: block ended

The convention, which you should follow, is 4 spaces per level. Any consistent amount works, but every line in a block must be indented by exactly the same amount. Never mix tabs and spaces; configure your editor to insert spaces when you press Tab.

else: the other path

else gives a block that runs when the condition is falsy. Exactly one of the two blocks runs:

age = int(input())
if age >= 18:
    print("adult")
else:
    print("minor")

else has no condition of its own, and it lines up with its if.

elif: many paths

For more than two cases, chain conditions with elif (short for "else if"):

n = int(input())
if n > 0:
    print("positive")
elif n < 0:
    print("negative")
else:
    print("zero")

Python tests the conditions in order and runs the block of the first one that is truthy. Once a branch runs, the rest of the chain is skipped entirely; their conditions are not even evaluated. If no condition is true, the else block runs (if there is one; it is optional).

      n > 0 ? --yes--> print("positive") ---------------+
        | no                                            |
      n < 0 ? --yes--> print("negative") ---------------+
        | no                                            |
      else    -------> print("zero") -------------------+
                                                        v
                                               continue after chain

Because only the first match runs, order matters when conditions overlap. Grading from high to low lets each test assume the higher ones failed:

score = int(input())
if score >= 90:
    grade = "A"
elif score >= 80:      # we already know score < 90 here
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"
print(grade)

Reversing the order (testing >= 70 first) would give every passing student a C.

Combining conditions

Conditions can use and, or, not and chained comparisons, all from the expressions module:

x = int(input())
if 1 <= x <= 10:
    print("in range")
if x < 0 or x > 100:
    print("out of bounds")
if not (x % 2 == 0):
    print("odd")

Prefer positive, readable conditions: if x % 2 == 1 reads better than if not (x % 2 == 0).

Nesting

A block can contain any statements, including another if. Each level adds 4 more spaces:

username = input()
password = input()
if username == "ada":
    if password == "engine":
        print("welcome")
    else:
        print("wrong password")
else:
    print("unknown user")

Nesting is sometimes the clearest structure, but deep nesting is hard to read. Often it can be flattened with and or with elif:

if username != "ada":
    print("unknown user")
elif password != "engine":
    print("wrong password")
else:
    print("welcome")

Both versions behave the same. The second reads as a list of cases, which is easier to scan.

pass: an empty block

A block cannot be empty; Python requires at least one statement. When you need a placeholder, use pass, a statement that does nothing:

if x > 0:
    pass   # TODO: handle positives
else:
    print("not positive")

The conditional expression

When you just need to choose between two values, there is a compact expression form:

age = 20
status = "adult" if age >= 18 else "minor"
print(status)
print(f"{age} is {'even' if age % 2 == 0 else 'odd'}")
adult
20 is even

Read A if condition else B as "A, if the condition holds, otherwise B". It is an expression (it produces a value), so it can go inside an f-string, a function call or an assignment. Use it for simple choices; for anything with side effects or more than two outcomes, a full if statement is clearer.

Common mistakes

Forgetting the colon.

if x > 3
    print("big")
  File "main.py", line 1
    if x > 3
            ^
SyntaxError: expected ':'

Inconsistent indentation.

if x > 3:
    print("big")
      print("really big")
  File "main.py", line 3
    print("really big")
IndentationError: unexpected indent

And a missing indent after a header gives IndentationError: expected an indented block after 'if' statement on line 1.

Using = instead of ==. if x = 3: is a SyntaxError (Python helpfully suggests Maybe you meant '==' or ':=' instead of '='?). = assigns; == compares.

Misreading or. if answer == "y" or "yes": is always true, because it means (answer == "y") or ("yes"), and the non-empty string "yes" is truthy. Write if answer == "y" or answer == "yes":, or if answer in ("y", "yes"):.

Overlapping conditions in the wrong order. As in the grade example, a broad condition placed first swallows the cases meant for later branches.

Recap

Going further: run import dis; dis.dis("if x > 0:\n y = 1\nelse:\n y = 2") and find the POP_JUMP_IF_FALSE instruction: at bytecode level, an if is a comparison followed by a conditional jump over the block.

Exercises

Exercise 1 · write code

Positive, negative or zero

Read a whole number and print exactly one word: positive, negative or zero.

File: main.py

Starting point

n = int(input())
if n > 0:
    print("positive")

Example

input

42

expected output

positive

Example

input

-7

expected output

negative
Hint 1

You need three branches: if, elif and else.

Hint 2

Only the first branch whose condition is true runs.

Reference solution
n = int(input())
if n > 0:
    print("positive")
elif n < 0:
    print("negative")
else:
    print("zero")

Exercise 2 · quiz

Which branch runs?

What does this print when score is 95?

score = 95
if score >= 50:
    print("pass")
elif score >= 90:
    print("excellent")
else:
    print("fail")
Answer
  • pass

Python checks the conditions top to bottom and runs only the first branch whose condition is true; the rest are skipped without even being evaluated. 95 >= 50 is true, so pass is printed and the elif is never reached. When conditions overlap, put the most specific one first: test >= 90 before >= 50.

Exercise 3 · write code

Letter grades

Read a whole-number score and print a letter grade:

scoreoutput
90 to 100A
80 to 89B
70 to 79C
60 to 69D
0 to 59F

If the score is below 0 or above 100, print invalid score instead.

File: main.py

Starting point

score = int(input())
if score >= 60:
    print("D")
elif score >= 90:
    print("A")
else:
    print("F")

Example

input

100

expected output

A

Example

input

90

expected output

A
Hint 1

Check for invalid input first, so the grade branches only see valid scores.

Hint 2

Order the grade checks from highest to lowest; then each elif only needs a lower bound.

Reference solution
score = int(input())
if score < 0 or score > 100:
    print("invalid score")
elif score >= 90:
    print("A")
elif score >= 80:
    print("B")
elif score >= 70:
    print("C")
elif score >= 60:
    print("D")
else:
    print("F")

Exercise 4 · write code

Leap years

Read a year (a positive whole number) and print YEAR is a leap year or YEAR is not a leap year, with the year in place of YEAR. For input 2024:

2024 is a leap year

The Gregorian rule: a year is a leap year if it is divisible by 4, except years divisible by 100, which are not, except years divisible by 400, which are. So 2024 and 2000 are leap years; 1900 and 2023 are not.

Use a conditional expression (X if condition else Y) or if/else, whichever you find clearer.

File: main.py

Starting point

year = int(input())
if year % 4 == 0:
    print(f"{year} is a leap year")
else:
    print(f"{year} is not a leap year")

Example

input

2024

expected output

2024 is a leap year

Example

input

2023

expected output

2023 is not a leap year
Hint 1

year % 4 == 0 tests divisibility by 4.

Hint 2

One way to write the rule: divisible by 4 and (not divisible by 100 or divisible by 400).

Hint 3

Test 1900 (not leap) and 2000 (leap): they are where simple solutions go wrong.

Reference solution
year = int(input())
leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
word = "a" if leap else "not a"
print(f"{year} is {word} leap year")

Exercise 5 · write code

Classify a triangle

Read three whole numbers on one line, separated by spaces: the side lengths of a triangle. Print one of:

For input 3 4 5 the output is scalene. You can read the three numbers with:

parts = input().split()
a = int(parts[0])
b = int(parts[1])
c = int(parts[2])

File: main.py

Starting point

parts = input().split()
a = int(parts[0])
b = int(parts[1])
c = int(parts[2])
if a == b == c:
    print("equilateral")
else:
    print("scalene")

Example

input

3 4 5

expected output

scalene

Example

input

2 2 2

expected output

equilateral
Hint 1

Rule out invalid triangles first, then classify.

Hint 2

Since equilateral is checked before isosceles, a == b or b == c or a == c is enough for isosceles.

Hint 3

Degenerate input such as 1 2 3 (1 + 2 equals 3) is not a triangle.

Reference solution
parts = input().split()
a = int(parts[0])
b = int(parts[1])
c = int(parts[2])
if a <= 0 or b <= 0 or c <= 0 or a >= b + c or b >= a + c or c >= a + b:
    print("not a triangle")
elif a == b == c:
    print("equilateral")
elif a == b or b == c or a == c:
    print("isosceles")
else:
    print("scalene")