Python Conditionals and Loops

AI-assistedDrafted with AI assistance and reviewed before publishing; examples are version-pinned. Verify against official docs when in doubt.

Python 3.13

Programs become useful when they can branch on conditions and repeat work without repetition in the source. This chapter covers both, plus the details that make Python’s version feel different from other languages.

Environment

Examples are verified on Python 3.13:

python --version
# Python 3.13.x

Save a snippet as flow.py and run python flow.py.

if / elif / else

score = 85

if score >= 90:
    print("excellent")
elif score >= 60:
    print("pass")
else:
    print("needs work")

Conditions are evaluated top to bottom and the first true branch wins. Order them from most to least specific: putting score >= 60 first would make the >= 90 branch unreachable.

Python has no switch-style fallthrough and no parentheses requirement. The colon and indentation are the syntax.

What counts as false

You rarely need len(items) > 0. These values are falsy:

for value in [None, False, 0, 0.0, "", [], (), {}, set()]:
    print(repr(value), "->", bool(value))

Everything else is truthy, including non-empty strings such as "0" and "False".

Two idioms that are easy to confuse:

if not items:          # false for None, [], "", 0
if items is None:      # true only for None

Use is None when “missing” must be distinguished from “empty”. Use not x when any empty value should be treated the same.

Conditional expressions and the walrus operator

status = "adult" if age >= 18 else "minor"

Since Python 3.8 you can also bind a name inside a condition:

if (count := len(items)) > 10:
    print(f"too many: {count}")

The walrus operator is worth using when the value is needed twice: once for the test, once in the body.

for loops

for iterates over an iterable, not over indices:

for name in ["python", "go", "rust"]:
    print(name)

When you need the position, use enumerate instead of range(len(...)):

for index, name in enumerate(["python", "go", "rust"], start=1):
    print(index, name)
# 1 python
# 2 go
# 3 rust

range is for counting, not for indexing into a sequence you already have:

for i in range(3):        # 0 1 2
    print(i)

list(range(1, 10, 2))     # [1, 3, 5, 7, 9]

Iterating two sequences in parallel uses zip, which stops at the shorter one:

for key, value in zip(["a", "b"], [1, 2]):
    print(key, value)

If the lengths differ, itertools.zip_longest fills the gaps instead of silently truncating.

while loops

count = 3
while count > 0:
    print(count)
    count -= 1

while True plus break is idiomatic for “read until the user is done”:

while True:
    line = input("type q to quit: ")
    if line == "q":
        break
    print(f"you typed: {line}")

Any while loop must contain something that eventually makes the condition false, or contain a break.

break, continue, and else

  • break leaves the loop entirely.
  • continue skips the rest of the current iteration.
  • else runs only if the loop finished without hitting break.
for n in [2, 4, 5, 6]:
    if n % 2 != 0:
        print(f"{n} is odd")
        break
else:
    print("all even")
# 5 is odd  -> the else block is skipped

The else clause is most useful for search loops: break on a hit, handle “not found” in else without a flag variable.

match statements

For structural matching, match (3.10+) is clearer than a chain of elif:

match command.split():
    case ["quit"]:
        print("bye")
    case ["load", filename]:
        print(f"loading {filename}")
    case _:
        print("unknown command")

case _ is the catch-all. Note that match compares structure, not values with ==, and a bare name in a pattern captures rather than compares.

Common mistakes

1. Mutating a list while iterating it

items = [1, 2, 3, 4]
for item in items:
    if item % 2 == 0:
        items.remove(item)   # index shifts; result depends on internals

Filter into a new list instead:

items = [item for item in items if item % 2 != 0]

2. Comparing values with is

a = 1000
b = 1000
a is b      # False: different objects
a == b      # True

Reserve is for singletons such as None, True, and False.

3. Forgetting to update the loop variable

A while loop whose condition never changes hangs the program. Ctrl+C interrupts it.

4. Mixing tabs and spaces

Python uses indentation as syntax. Standardize on four spaces and enable “convert tabs to spaces” in your editor.

5. Shadowing built-ins

list = [1, 2] breaks every later call to list(). Same for dict, str, id.

Summary

  • Truthiness covers empty containers, 0, and None; use is None for missing values.
  • for iterates values; enumerate gives positions, range gives counts, zip pairs sequences.
  • break exits, continue skips, loop else runs only without break.
  • Never add or remove items from a list you are iterating.

Next: strings, slicing, and formatting.