Control Flow

Conditionals, loops, and branching logic.

Conditionals

if / elif / else

age = 25

if age < 18:
    print("Minor")
elif age < 65:
    print("Adult")
else:
    print("Senior")

Comparison Operators

OperatorMeaning
==Equal
!=Not equal
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal
isSame object
is notDifferent object
inMembership
not inNon-membership

Combining Conditions

# and - both must be true
if age >= 18 and age < 65:
    print("Working age")

# or - either can be true
if day == "Saturday" or day == "Sunday":
    print("Weekend")

# not - negation
if not is_logged_in:
    print("Please log in")

# Chained comparisons
if 18 <= age < 65:       # Same as: age >= 18 and age < 65
    print("Working age")

if a == b == c:          # All equal
    print("All same")

Ternary Expression

# condition_true if condition else condition_false
status = "adult" if age >= 18 else "minor"

# Can be nested (but avoid deep nesting)
size = "large" if x > 100 else "medium" if x > 50 else "small"

Truthiness in Conditionals

# These are all equivalent
if items:                # Pythonic
if len(items) > 0:       # Explicit but verbose
if items != []:          # Avoid

# Check for None specifically
if x is None:            # Correct
if x == None:            # Works but not idiomatic
if not x:                # Wrong - also catches 0, "", [], etc.

Match Statement (3.10+)

Pattern matching - more powerful than switch/case:

Basic Matching

def http_status(code):
    match code:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Server Error"
        case _:                    # Wildcard (default)
            return "Unknown"

Pattern Matching

# Match multiple values
match code:
    case 200 | 201 | 204:
        return "Success"
    case 400 | 401 | 403 | 404:
        return "Client Error"

# Match with guards
match point:
    case (x, y) if x == y:
        print("On diagonal")
    case (x, y):
        print(f"Point at {x}, {y}")

# Match structure
match data:
    case {"type": "user", "name": name}:
        print(f"User: {name}")
    case {"type": "error", "message": msg}:
        print(f"Error: {msg}")
    case [first, *rest]:
        print(f"List starting with {first}")

# Match class instances
match event:
    case Click(x, y):
        handle_click(x, y)
    case KeyPress(key):
        handle_key(key)

Loops

for Loop

Iterates over any iterable:

# List
for item in [1, 2, 3]:
    print(item)

# String
for char in "hello":
    print(char)

# Range
for i in range(5):         # 0, 1, 2, 3, 4
    print(i)

for i in range(2, 8):      # 2, 3, 4, 5, 6, 7
    print(i)

for i in range(0, 10, 2):  # 0, 2, 4, 6, 8
    print(i)

for i in range(10, 0, -1): # 10, 9, 8, ... 1
    print(i)

# Dictionary
for key in my_dict:
    print(key)

for key, value in my_dict.items():
    print(f"{key}: {value}")

# With index
for i, item in enumerate(items):
    print(f"{i}: {item}")

for i, item in enumerate(items, start=1):  # Start from 1
    print(f"{i}: {item}")

# Multiple iterables
for a, b in zip(list1, list2):
    print(f"{a} - {b}")

while Loop

count = 0
while count < 5:
    print(count)
    count += 1

# With condition
while True:
    line = input()
    if line == "quit":
        break
    print(line)

Loop Control

# break - exit loop entirely
for i in range(10):
    if i == 5:
        break
    print(i)  # 0, 1, 2, 3, 4

# continue - skip to next iteration
for i in range(5):
    if i == 2:
        continue
    print(i)  # 0, 1, 3, 4

# else - runs if loop completes without break
for item in items:
    if item == target:
        print("Found!")
        break
else:
    print("Not found")  # Only runs if no break

Nested Loops

for i in range(3):
    for j in range(3):
        print(f"({i}, {j})")

# Breaking out of nested loops
found = False
for i in range(10):
    for j in range(10):
        if condition:
            found = True
            break
    if found:
        break

# Cleaner with a function
def find_match():
    for i in range(10):
        for j in range(10):
            if condition:
                return (i, j)
    return None

Iterables and Iterators

Common Iterables

# Sequences
list, tuple, str, range, bytes

# Collections
dict, set, frozenset

# Files
for line in open("file.txt"):
    print(line)

# Generators
(x**2 for x in range(10))

Useful Iteration Functions

# enumerate - index + value
for i, val in enumerate(['a', 'b', 'c']):
    print(i, val)  # 0 a, 1 b, 2 c

# zip - parallel iteration
for a, b in zip([1, 2, 3], ['a', 'b', 'c']):
    print(a, b)  # 1 a, 2 b, 3 c

# reversed - iterate backwards
for x in reversed([1, 2, 3]):
    print(x)  # 3, 2, 1

# sorted - iterate in order
for x in sorted([3, 1, 2]):
    print(x)  # 1, 2, 3

# filter - only matching items
for x in filter(lambda n: n > 0, [-1, 0, 1, 2]):
    print(x)  # 1, 2

# map - transform each item
for x in map(str.upper, ['a', 'b', 'c']):
    print(x)  # A, B, C

Unpacking in Loops

# Tuple unpacking
points = [(1, 2), (3, 4), (5, 6)]
for x, y in points:
    print(f"x={x}, y={y}")

# Dict items
for key, value in my_dict.items():
    print(f"{key}: {value}")

# Star unpacking
data = [(1, 2, 3, 4), (5, 6, 7, 8)]
for first, *middle, last in data:
    print(first, middle, last)  # 1 [2, 3] 4

Common Patterns

Early Return / Guard Clauses

# Instead of nested ifs
def process(data):
    if data is None:
        return None
    if not data:
        return []
    if not is_valid(data):
        raise ValueError("Invalid data")

    # Main logic here (not nested)
    return transform(data)

Loop with Else

# Search pattern
def find_item(items, target):
    for item in items:
        if item == target:
            return item
    else:
        return None  # Not found

# Prime check
def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False
    return True

Infinite Loop with Break

# Menu pattern
while True:
    choice = input("Enter choice (q to quit): ")
    if choice == 'q':
        break
    process_choice(choice)

Flag Variables

# Track state across iterations
found = False
for item in items:
    if matches(item):
        found = True
        break

if found:
    print("Found a match")

Practice

# 1. FizzBuzz
for i in range(1, 101):
    if i % 15 == 0:
        print("FizzBuzz")
    elif i % 3 == 0:
        print("Fizz")
    elif i % 5 == 0:
        print("Buzz")
    else:
        print(i)

# 2. Find first match
def first_even(numbers):
    for n in numbers:
        if n % 2 == 0:
            return n
    return None

# 3. Count occurrences
text = "hello world"
counts = {}
for char in text:
    counts[char] = counts.get(char, 0) + 1

# 4. Nested loop - multiplication table
for i in range(1, 11):
    for j in range(1, 11):
        print(f"{i*j:4}", end="")
    print()