Skip to content

Control Flow

Control flow simply decides which line of code runs, and when it runs. It helps your program:

  • make decisions based on conditions
  • repeat tasks again and again without writing the same code many times
  • stop or skip certain steps whenever needed
  • handle different situations depending on what’s happening

These statements help you make decisions in your code and control what happens next.

  • if -> checks a condition (runs the code inside if it is True)
  • elif -> checks one more condition, but only if the first one was false
  • else -> runs only if none of the above conditions were true

Order matters here: elif and else parts are optional, and you can add as many elif blocks as you want, one after another.

x = 10
if x > 10:
print("Greater than 10")
elif x == 10:
print("Equal to 10")
else:
print("Less than 10")

Good Habit: Use elif when you have multiple conditions to check, instead of writing if statements inside other if statements. It’s much easier to read this way.

graph TD Start -->|Input| x["x = ?"] x --> C1{"x > 10"} C1 -->|True| A["✓ Greater than 10"] C1 -->|False| C2{"x == 10"} C2 -->|True| B["✓ Equal to 10"] C2 -->|False| C["✓ Less than 10"]

This is just a short and quick way to write a simple if-else statement in just one line. It’s also called a conditional expression.

age = 18
status = "Adult" if age >= 18 else "Minor"
print(status) # Output: Adult

How it’s written: value_if_true if condition else value_if_false

When to Use It:

  • When you want to assign a value based on a simple condition
  • When you want to set a default value
  • When you want to avoid writing long if-else blocks for small things

Watch Out: Try not to put one ternary operator inside another. It makes the code confusing and hard to read.

graph TD A["condition"] -->|True| B["value_if_true"] A -->|False| C["value_if_false"]

This lets you compare one value against several patterns to find a match. This is only available in Python 3.10 and above.

def check(value):
match value:
case 1:
return "One"
case 2:
return "Two"
case _:
return "Other"
print(check(2)) # Output: Two

Main Points:

  • case pattern: -> matches one specific value
  • _ -> this is the default case (it matches anything that didn’t match before)
  • This is easier to read than writing a long chain of if-elif-else statements
  • It also runs faster when you have a lot of conditions to check

More Advanced Patterns:

match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"On Y axis: {y}")
case (x, 0):
print(f"On X axis: {x}")
case _:
print("General point")
graph TD Start[( value )] -->|case 1| One["-> One"] Start -->|case 2| Two["-> Two"] Start -->|default _| Default["-> Other"]

A for loop goes through each item in a sequence, one at a time. It’s the loop you’ll use the most in Python.

for i in range(3):
print(i)

Output:

0
1
2

Going Through a Collection:

names = ["A", "B", "C"]
for name in names:
print(name)

Good Habits to Follow:

  • Use a for loop instead of a while loop whenever you’re going through a sequence
  • Use enumerate() when you need both the position (index) and the value together
  • Use zip() when you need to go through more than one sequence at the same time
  • Use clear and meaningful names for your variables: write for user in users: instead of for u in users:
graph TD Start --> Init["i ∈ [0,1,2]"] Init --> i0["i=0"] i0 --> Print0["↓ print"] Print0 --> i1["i=1"] i1 --> Print1["↓ print"] Print1 --> i2["i=2"] i2 --> Print2["↓ print"] Print2 --> End["✓ Done"]

A while loop keeps running again and again, until the condition turns false.

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

Main Difference Between for and while:

  • for -> use this when you already know what you’re looping through
  • while -> use this when you want to keep looping until something becomes false
  • Be careful, because if the condition never becomes false, the loop will run forever

Common Ways It’s Used:

# Input validation
while True:
user_input = input("Enter a number: ")
if user_input.isdigit():
break
# Event loop simulation
running = True
while running:
handle_events()
update()
render()
graph TD Start --> Condition{"count < 3"} Condition -->|✓ Yes| Print["print & increment"] Print -->|Loop| Condition Condition -->|✗ No| End["✓ Exit"]

These statements let you control how your loop behaves in more detailed situations.

This stops the loop right away and exits it completely.

for i in range(5):
if i == 3:
break # Exits loop when i equals 3
print(i)

Output: 0 1 2

This skips the current round of the loop and moves straight to the next one.

for i in range(5):
if i == 2:
continue # Skips i=2
print(i)

Output: 0 1 3 4

This does absolutely nothing when it runs. It’s mainly used as a placeholder, just to keep the code valid.

for i in range(5):
if i == 2:
pass # Placeholder, does nothing
if i == 3:
pass # Also valid
print(i)

This part only runs if the loop finishes completely on its own without hitting a break.

for i in range(3):
print(i)
else:
print("Loop finished normally")
# Search example
for user in users:
if user.id == target_id:
print("User found")
break
else:
print("User not found")
graph TD Start["Start: for i in range(5)"] --> LoopCheck{"Next item exists?"} LoopCheck -->|No| ElseBlock["Run else block"] ElseBlock --> End["End"] LoopCheck -->|Yes| Body["Execute Loop Body"] Body --> Condition{"i == 3?"} Condition -->|Yes| Break["break -> Exit Loop"] Break --> EndBreak["End (else NOT executed)"] Condition -->|No| Continue["Continue Loop"] Continue --> LoopCheck

range() is used to create a sequence of numbers easily and efficiently. You’ll see it used in loops all the time.

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

Three Ways to Use It:

  1. range(end) -> starts from 0 and goes up to end-1

    range(5) # 0, 1, 2, 3, 4
  2. range(start, end) -> starts from start and goes up to end-1

    range(1, 6) # 1, 2, 3, 4, 5
  3. range(start, end, step) -> starts from start, goes up to end-1, and jumps ahead by step each time

    range(0, 10, 2) # 0, 2, 4, 6, 8
    range(10, 0, -2) # 10, 8, 6, 4, 2 (reverse)

Good Things to Know:

  • range() doesn’t actually create the full list right away, it creates the numbers only when needed, which saves memory
  • If you need an actual list, you can convert it like this: list(range(5))
  • You can also use a negative step number to count backwards

This adds a position number (index) to each item while you’re looping through it. It’s really useful when you need both the position and the value at the same time.

items = ["a", "b", "c"]
for index, value in enumerate(items):
print(index, value)

Output:

0 a
1 b
2 c

Changing the Starting Number:

for index, value in enumerate(items, start=1):
print(index, value)

Output:

1 a
2 b
3 c

A Real-Life Example:

tasks = ["buy milk", "pay bills", "exercise"]
for num, task in enumerate(tasks, 1):
print(f"{num}. {task}")
# Output:
# 1. buy milk
# 2. pay bills
# 3. exercise
graph TD A["['a','b','c']"] --> B["enumerate()"] B --> C["(0, 'a')"] B --> D["(1, 'b')"] B --> E["(2, 'c')"]

This joins multiple sequences together, matching up items based on their position. It’s great when you want to loop through more than one list at the same time.

names = ["A", "B"]
scores = [90, 80]
for name, score in zip(names, scores):
print(name, score)

Output:

A 90
B 80

Things to Remember:

  • It stops as soon as the shortest sequence runs out of items
  • It works with any kind of sequence, like lists, tuples, strings, and so on
names = ["A", "B", "C"]
scores = [90, 80] # Only 2 elements
for name, score in zip(names, scores):
print(name, score)
# Output:
# A 90
# B 80
# C is skipped because no corresponding score

Useful Examples:

# Merge multiple lists
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
combined = list(zip(list1, list2))
# Output: [(1, 'a'), (2, 'b'), (3, 'c')]
# Process related data in parallel
students = ["Alice", "Bob", "Charlie"]
gpa = [3.8, 3.5, 3.9]
for student, grade in zip(students, gpa):
print(f"{student}: {grade}")
graph TD A["names: ['A','B']"] --> Z["zip()"] B["scores: [90,80]"] --> Z Z --> P1["('A', 90)"] Z --> P2["('B', 80)"]