Conditions

A program that always does the same thing is of limited use. Real programs decide: charge shipping only below a certain order total, warn only when the disk is almost full. The order in which statements run is called the program's control flow, and the simplest way to change it is to run some code only when something is true.

That "something" is a condition: an expression that Python evaluates to decide which way to go. Usually it is a comparison, which produces True or False:

temperature = 23
print(temperature > 20)
print(temperature == 30)
print(type(temperature > 20))
True
False
<class 'bool'>

You can combine comparisons with and, or and not, and chain them, as in 0 <= percent <= 100. The page on operators and expressions covers all of these in detail. As you will see below, a condition does not even have to be a bool: any value will do.

The if statement

An if statement consists of the keyword if, a condition, a colon, and an indented block. When the condition is true, Python runs the block; when it is false, Python skips it and continues after it.

temperature = 31

if temperature > 30:
    print("It's hot today.")
    print("Drink plenty of water.")

print("Have a nice day.")
It's hot today.
Drink plenty of water.
Have a nice day.

With temperature = 25, only the last line would be printed: it is not indented, so it is not part of the if.

Blocks and indentation

A block is a group of statements that belong together and run as a unit. In Python, a block starts after a line that ends in a colon, and consists of the lines below it that are indented further — four spaces by convention. The block ends at the first line that is indented less again. Every line in one block must have exactly the same indentation.

A block can never be empty. If you leave it out, Python refuses to run the program at all:

stock = 0
if stock == 0:
print("Sold out")
IndentationError: expected an indented block after 'if' statement on line 2

See indentation for the general rules, and the pass statement below for how to write a block that does nothing.

else

An else clause gives the alternative: its block runs when the condition is false. It has no condition of its own, and it is written at the same indentation as its if. Exactly one of the two blocks runs.

age = 16

if age >= 18:
    print("You may vote.")
else:
    print("You may not vote yet.")
You may not vote yet.

elif

For more than two possibilities, add one or more elif clauses (short for "else if"). Python tests the conditions from top to bottom and runs the block of the first one that is true. It then skips the rest of the chain, even if later conditions are also true. The optional else at the end catches every case that no condition matched.

score = 74

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"

print("Grade:", grade)
Grade: C
x = 10

if x > 5:
    print("more than 5")
if x > 8:
    print("more than 8")
else:
    print("8 or less")
more than 5
more than 8

Nested if statements

A block can contain any statements, including another if. This is called nesting. The inner if is only reached when the outer condition is true, and each level is indented one step further.

has_ticket = True
age = 15

if has_ticket:
    if age >= 16:
        print("Enjoy the film.")
    else:
        print("You need an adult with you.")
else:
    print("Please buy a ticket first.")
You need an adult with you.

Indentation decides which if an else belongs to: the inner else lines up with the inner if, the outer one with the outer if. Deep nesting quickly becomes hard to read. Often you can flatten it by combining conditions with and, or by using elif:

has_ticket = True
age = 15

if not has_ticket:
    print("Please buy a ticket first.")
elif age >= 16:
    print("Enjoy the film.")
else:
    print("You need an adult with you.")
You need an adult with you.

Truthy and falsy values

A condition does not have to be True or False. Python accepts any value and asks it whether it counts as true. This is called truthiness: a value that counts as true is truthy, a value that counts as false is falsy. The built-in function bool() shows the answer Python uses.

The rule is short: these values are falsy —

  • False and None;
  • zero of every numeric type: 0, 0.0, 0j (and also Decimal(0) and Fraction(0, 1));
  • every empty collection: "", [], (), {}, set() and range(0);
  • objects of your own classes that say so, through a __bool__() method that returns False or a __len__() method that returns 0.

Everything else is truthy.

values = [0, 0.0, "", [], {}, None, False, 42, -1, "0", " ", [0], "False"]

for value in values:
    print(repr(value), "->", bool(value))
0 -> False
0.0 -> False
'' -> False
[] -> False
{} -> False
None -> False
False -> False
42 -> True
-1 -> True
'0' -> True
' ' -> True
[0] -> True
'False' -> True

Truthiness makes short, readable checks possible: "if there is a name", "if the list has items".

name = ""
basket = ["bread", "milk"]

if name:
    print("Hello,", name)
else:
    print("Name is missing.")

if basket:
    print("Items in basket:", len(basket))
Name is missing.
Items in basket: 2

The conditional expression

Sometimes an if/else only chooses between two values. For that, Python has the conditional expression (other languages call it the ternary operator):

value_if_true if condition else value_if_false

Python first evaluates the condition, then evaluates and returns only the value on the chosen side. Because it is an expression, not a statement, you can use it anywhere a value is expected: on the right of =, as an argument, inside an f-string.

stock = 0
status = "in stock" if stock > 0 else "sold out"
print(status)

temperature = 12
print("Wear a coat." if temperature < 15 else "A T-shirt is fine.")

items = 1
print(f"{items} {'item' if items == 1 else 'items'} in your cart")
sold out
Wear a coat.
1 item in your cart

The else part is required: x if c on its own is a SyntaxError. Keep conditional expressions short. If you are tempted to nest them, write a normal if statement instead.

The pass statement

The pass statement does nothing at all. It exists because Python's syntax requires at least one statement in every block. Use it as a placeholder while you are still writing the code, or to state explicitly that a case needs no action:

order_total = 80

if order_total >= 100:
    pass  # TODO: add the free gift
else:
    print("Add", 100 - order_total, "to get a free gift.")
Add 20 to get a free gift.

pass works in every kind of block: loops, functions and classes as well. A comment alone is not enough, because Python ignores comments — a block containing only a comment is still empty.

Pattern matching with match

The match statement, added in Python 3.10, compares one value — the subject — against a series of patterns. Each pattern sits in a case clause. Python tries the cases from top to bottom and runs the block of the first one that matches; the others are skipped. If no case matches, nothing happens.

It resembles the switch statement of other languages, but there is no fall-through (no break is needed) and the patterns can do much more than compare with a constant. match and case are soft keywords: they only have a special meaning in this statement, so they can still be used as ordinary names elsewhere.

Literal patterns and alternatives

The simplest pattern is a literal, such as a number or a string: it matches when the subject is equal to it. The | symbol joins alternatives ("this or that"), and the wildcard _ matches anything, so case _: at the end plays the role of else.

for status in [200, 302, 404, 500]:
    match status:
        case 200:
            print(status, "OK")
        case 301 | 302:
            print(status, "Redirect")
        case 404:
            print(status, "Not found")
        case _:
            print(status, "Other status")
200 OK
302 Redirect
404 Not found
500 Other status

Capture patterns and the wildcard

A bare name in a pattern is a capture pattern. It does not compare anything: it matches every value and binds it to that name, so the block can use it. The wildcard _ also matches everything, but binds nothing.

Because a bare capture pattern always matches, it may only appear in the last case. Anywhere else, the cases after it could never be reached, and Python reports a SyntaxError.

Sequence patterns

A pattern in square brackets (or parentheses) matches a list or tuple with the same number of items, where each item matches its own sub-pattern. Literals inside must be equal, names capture the item in that position, and *name captures all remaining items as a list. This makes match a natural fit for simple commands:

commands = [["quit"], ["go", "north"], ["drop", "lamp", "key"], ["dance"]]

for command in commands:
    match command:
        case ["quit"]:
            print("Goodbye")
        case ["go", direction]:
            print("Going", direction)
        case ["drop", *items]:
            print("Dropping", items)
        case _:
            print("Unknown command:", command)
Goodbye
Going north
Dropping ['lamp', 'key']
Unknown command: ['dance']

Guards

A guard is an if condition after a pattern: case pattern if condition:. The case only matches when the pattern matches and the condition is true. Guards often go together with a capture pattern, to test the captured value.

for temperature in [-5, 0, 18, 35]:
    match temperature:
        case 0:
            print(temperature, "freezing point")
        case t if t < 0:
            print(t, "below freezing")
        case t if t >= 30:
            print(t, "hot")
        case t:
            print(t, "mild")
-5 below freezing
0 freezing point
18 mild
35 hot

Patterns can also match dictionaries, objects of a class, and combinations of all of these. For everyday decisions on a single number or string, though, if/elif is often just as clear; match shines when you need to check the shape of a value and take it apart at the same time.