Control Structures

Making decisions and repeating actions with conditionals and loops.

Introduction

Control structures allow your programs to make decisions and repeat actions. Conditional statements (if, elif, else) let your code choose different paths based on conditions, while loops (for and while) enable you to execute code repeatedly. Mastering these concepts is essential for writing useful, dynamic programs that can handle different situations and process collections of data.

Conditional Statements: if/elif/else

Conditional statements allow your program to execute different code blocks based on whether conditions are true or false.

Simple if Statement

age = 20

if age >= 18:
    print("You are an adult")
    print("You can vote")

# Only executes if condition is True
# Note the colon (:) and indentation!
Expected Output:
You are an adult
You can vote

if/else Statement

age = 16

if age >= 18:
    print("You are an adult")
else:
    print("You are a minor")
    print("You cannot vote")

# Executes one block or the other, but not both
Expected Output:
You are a minor
You cannot vote

if/elif/else Statement

Use elif (else if) to check multiple conditions:

score = 85

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

print(f"Your grade is {grade}")

# Checks conditions in order
# Stops at first True condition
# Only one block executes
Expected Output:
Your grade is B

How the grade conditions are checked, top to bottom

startscore >= 90?grade = "A"score >= 80?grade = "B"grade = "F"elseprint gradeTrueFalseTrueFalse

Figure 1: Python checks each condition in order and stops at the first True one (shown here trimmed to three branches).

Nested Conditionals

age = 20
has_license = True

if age >= 18:
    if has_license:
        print("You can drive")
    else:
        print("You need a license to drive")
else:
    print("You must be 18 to drive")

# Inner if/else inside outer if
Expected Output:
You can drive

Multiple Conditions

temperature = 25
is_sunny = True

# Using and
if temperature > 20 and is_sunny:
    print("Perfect weather for a picnic")

# Using or
if temperature < 0 or temperature > 35:
    print("Extreme weather conditions")

# Using not
if not is_sunny:
    print("It's cloudy today")

# Combining operators
if (temperature >= 15 and temperature <= 25) or is_sunny:
    print("Good weather for outdoor activities")
Expected Output:
Perfect weather for a picnic
Good weather for outdoor activities

Ternary Operator (Conditional Expression)

A concise way to write simple if/else statements:

age = 20

# Traditional way
if age >= 18:
    status = "adult"
else:
    status = "minor"

# Ternary operator (one line)
status = "adult" if age >= 18 else "minor"
print(status)

# Syntax: value_if_true if condition else value_if_false
a, b = 10, 20
max_value = a if a > b else b
print(max_value)
Expected Output:
adult
20

Important Note: Using long chains of if/elif/else statements can lead to code that is hard to read and maintain. In real-world, production-ready programs, there are better patterns and techniques to handle complex decision logic. We will discuss these approaches later in the course!

For Loops

For loops iterate over a sequence (like a list, string, or range) and execute code for each item.

Basic For Loop

# Loop through a list
fruits = ["apple", "banana", "orange"]

for fruit in fruits:
    print(fruit)

# Loop through a string
for char in "Hello":
    print(char)
Expected Output:
apple
banana
orange
H
e
l
l
o

Using range()

# range(stop) - 0 to stop-1
for i in range(5):
    print(i)

# range(start, stop) - start to stop-1
for i in range(2, 6):
    print(i)

# range(start, stop, step) - with step size
for i in range(0, 10, 2):
    print(i)

# Countdown
for i in range(5, 0, -1):
    print(i)
Expected Output:
0
1
2
3
4
2
3
4
5
0
2
4
6
8
5
4
3
2
1

Looping Through Lists

numbers = [1, 2, 3, 4, 5]

# Loop through values
for num in numbers:
    print(num * 2)

# Loop with index using enumerate()
for index, value in enumerate(numbers):
    print(f"Index {index}: {value}")

# Modify list while looping (create new list)
squares = []
for num in numbers:
    squares.append(num ** 2)
print(squares)
Expected Output:
2
4
6
8
10
Index 0: 1
Index 1: 2
Index 2: 3
Index 3: 4
Index 4: 5
[1, 4, 9, 16, 25]

Looping Through Dictionaries

person = {"name": "Alice", "age": 30, "city": "New York"}

# Loop through keys (default)
for key in person:
    print(key, person[key])

# Loop through keys explicitly
for key in person.keys():
    print(key)

# Loop through values
for value in person.values():
    print(value)

# Loop through key-value pairs
for key, value in person.items():
    print(f"{key}: {value}")
Expected Output:
name Alice
age 30
city New York
name
age
city
Alice
30
New York
name: Alice
age: 30
city: New York

Nested For Loops

# Multiplication table
for i in range(1, 4):
    for j in range(1, 4):
        print(f"{i} x {j} = {i * j}")
    print()  # Blank line between tables

# Loop through 2D list
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

for row in matrix:
    for value in row:
        print(value, end=" ")
    print()  # New line after each row
Expected Output:
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3

2 x 1 = 2
2 x 2 = 4
2 x 3 = 6

3 x 1 = 3
3 x 2 = 6
3 x 3 = 9

1 2 3 
4 5 6 
7 8 9 
Tip: Try to avoid having nested loops more than 2 levels deep. Deeply nested loops (three or more) make code harder to understand, maintain, and debug. They can also cause performance problems for large input sizes.
  • If you find yourself writing loops inside loops inside loops (three or more levels), consider refactoring your code.
  • Look for alternatives like breaking code into functions, using helper data structures, or leveraging built-in functions such as zip(), enumerate(), itertools utilities, or comprehensions.
  • Sometimes, complex problems can be solved more elegantly by flattening data or by using recursion or external libraries.
from itertools import product

sizes = ["S", "M"]
colors = ["red", "blue"]
styles = ["crew", "v-neck"]

# Discouraged: three levels of nesting just to build combinations
for size in sizes:
    for color in colors:
        for style in styles:
            print(f"{size} {color} {style}")

print("---")

# Better: itertools.product produces the same combinations from one loop
for size, color, style in product(sizes, colors, styles):
    print(f"{size} {color} {style}")
Expected Output:
S red crew
S red v-neck
S blue crew
S blue v-neck
M red crew
M red v-neck
M blue crew
M blue v-neck
---
S red crew
S red v-neck
S blue crew
S blue v-neck
M red crew
M red v-neck
M blue crew
M blue v-neck

While Loops

While loops continue executing as long as a condition is True. They're useful when you don't know beforehand how many iterations you need.

Basic While Loop

# Count from 1 to 5
count = 1
while count <= 5:
    print(count)
    count += 1  # Increment counter (important!)

# Sum numbers until user enters 0
total = 0
num = int(input("Enter a number (0 to stop): "))
while num != 0:
    total += num
    num = int(input("Enter a number (0 to stop): "))
print(f"Total: {total}")
Expected Output:
1
2
3
4
5
Enter a number (0 to stop): 5
Enter a number (0 to stop): 10
Enter a number (0 to stop): 0
Total: 15

Infinite Loops

Warning: Be careful with while loops! If the condition never becomes False, you'll create an infinite loop. Always ensure the condition will eventually become False, or use break to exit.

# This creates an infinite loop (DON'T RUN!)
# count = 1
# while count > 0:
#     print(count)
#     count += 1  # Condition never becomes False!

# Correct: condition eventually becomes False
count = 5
while count > 0:
    print(count)
    count -= 1  # Decrement - condition will become False
Expected Output:
5
4
3
2
1

While vs For Loop

Use While Loop When:
  • You don't know iterations in advance
  • Looping until a condition is met
  • User input validation
  • Processing until sentinel value
  • Event-driven loops
Use For Loop When:
  • Iterating over a sequence
  • Known number of iterations
  • Processing all items in collection
  • More readable for sequences
  • Pythonic way for collections

Break and Continue Statements

break and continue give you more control over loop execution.

Break Statement

break exits the loop immediately, even if the condition is still True:

# Find first even number
numbers = [1, 3, 5, 8, 9, 10]

for num in numbers:
    if num % 2 == 0:
        print(f"First even number: {num}")
        break  # Exit immediately, 9 and 10 are never checked
    print(f"{num} is odd")

# Break in while loop
while True:
    user_input = input("Enter 'quit' to exit: ")
    if user_input.lower() == 'quit':
        break  # Exit infinite loop
    print(f"You entered: {user_input}")
Expected Output:
1 is odd
3 is odd
5 is odd
First even number: 8
Enter 'quit' to exit: hello
You entered: hello
Enter 'quit' to exit: quit

Continue Statement

continue skips the rest of the current iteration and moves to the next:

# Print only even numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8]

for num in numbers:
    if num % 2 != 0:  # If odd
        continue  # Skip to next iteration, 1/3/5/7 never reach the print
    print(num)

# Skip negative numbers
numbers = [5, -2, 8, -1, 9]

for num in numbers:
    if num < 0:
        continue
    print(f"Processing {num}")
Expected Output:
2
4
6
8
Processing 5
Processing 8
Processing 9

Else Clause with Loops

You can use else with loops. The else block executes only if the loop completes normally (not exited with break):

# Search for a value
numbers = [1, 3, 5, 7, 9]
search_for = 4

for num in numbers:
    if num == search_for:
        print(f"Found {search_for}!")
        break
else:
    # Executes only if loop completes without break
    print(f"{search_for} not found in list")

# If search_for = 5, prints "Found 5!" and else doesn't execute
# If search_for = 4, prints "4 not found in list"
Expected Output:
4 not found in list

Looping Through Data Structures

Here are comprehensive examples of looping through different data structures:

Lists

fruits = ["apple", "banana", "orange"]

# Simple iteration
for fruit in fruits:
    print(fruit.upper())

# With index
for i, fruit in enumerate(fruits):
    print(f"{i+1}. {fruit}")

# With condition
for fruit in fruits:
    if len(fruit) > 5:
        print(f"{fruit} has more than 5 characters")
Expected Output:
APPLE
BANANA
ORANGE
1. apple
2. banana
3. orange
banana has more than 5 characters
orange has more than 5 characters

Tuples

coordinates = [(0, 0), (1, 2), (3, 4), (5, 6)]

# Unpacking during iteration
for x, y in coordinates:
    print(f"Point at ({x}, {y})")

# With index
for i, (x, y) in enumerate(coordinates):
    distance = (x**2 + y**2)**0.5
    print(f"Point {i}: ({x}, {y}), distance from origin: {distance:.2f}")
Expected Output:
Point at (0, 0)
Point at (1, 2)
Point at (3, 4)
Point at (5, 6)
Point 0: (0, 0), distance from origin: 0.00
Point 1: (1, 2), distance from origin: 2.24
Point 2: (3, 4), distance from origin: 5.00
Point 3: (5, 6), distance from origin: 7.81

Dictionaries

student_grades = {
    "Alice": 85,
    "Bob": 92,
    "Charlie": 78,
    "Diana": 95
}

# Loop through keys
for name in student_grades:
    print(name, student_grades[name])

# Loop through items (key-value pairs) - recommended
for name, grade in student_grades.items():
    status = "Pass" if grade >= 80 else "Fail"
    print(f"{name}: {grade} ({status})")

# Loop through values only
total = 0
for grade in student_grades.values():
    total += grade
average = total / len(student_grades)
print(f"Average grade: {average}")
Expected Output:
Alice 85
Bob 92
Charlie 78
Diana 95
Alice: 85 (Pass)
Bob: 92 (Pass)
Charlie: 78 (Fail)
Diana: 95 (Pass)
Average grade: 87.5

Sets

unique_numbers = {1, 2, 3, 4, 5}

# Sets have no defined order, so wrap them in sorted() whenever the
# order of the output matters
for num in sorted(unique_numbers):
    print(num ** 2)

# Process only even numbers
for num in sorted(unique_numbers):
    if num % 2 == 0:
        print(f"{num} is even")
Expected Output:
1
4
9
16
25
2 is even
4 is even

Nested Structures

# List of dictionaries
students = [
    {"name": "Alice", "grades": [85, 90, 88]},
    {"name": "Bob", "grades": [78, 82, 80]},
    {"name": "Charlie", "grades": [92, 95, 93]}
]

for student in students:
    name = student["name"]
    grades = student["grades"]
    average = sum(grades) / len(grades)
    print(f"{name}'s average: {average:.1f}")

# Dictionary with list values
data = {
    "fruits": ["apple", "banana"],
    "vegetables": ["carrot", "broccoli"],
    "grains": ["rice", "wheat"],
}

for category, items in data.items():
    print(f"{category}: {', '.join(items)}")
Expected Output:
Alice's average: 87.7
Bob's average: 80.0
Charlie's average: 93.3
fruits: apple, banana
vegetables: carrot, broccoli
grains: rice, wheat

Practical Examples

Example: Number Guessing Game

import random

secret_number = random.randint(1, 100)
max_attempts = 5
attempts = 0

print("I'm thinking of a number between 1 and 100.")
print(f"You have {max_attempts} attempts.")

while attempts < max_attempts:
    guess = int(input("Enter your guess: "))
    attempts += 1

    if guess == secret_number:
        tries = "attempt" if attempts == 1 else "attempts"
        print(f"Congratulations! You guessed it in {attempts} {tries}!")
        break
    elif guess < secret_number:
        print("Too low!")
    else:
        print("Too high!")

    remaining = max_attempts - attempts
    if remaining > 0:
        # Ternary operator again: "1 attempt", but "2 attempts"
        label = "attempt" if remaining == 1 else "attempts"
        print(f"You have {remaining} {label} remaining.")
else:
    print(f"Game over! The number was {secret_number}.")
Expected Output:
I'm thinking of a number between 1 and 100.
You have 5 attempts.
Enter your guess: 34
Too high!
You have 4 attempts remaining.
Enter your guess: 11
Too high!
You have 3 attempts remaining.
Enter your guess: 4
Too low!
You have 2 attempts remaining.
Enter your guess: 6
Too high!
You have 1 attempt remaining.
Enter your guess: 5
Congratulations! You guessed it in 5 attempts!

Example: Data Processing

# Process student data
students = {
    "Alice": [85, 90, 88],
    "Bob": [78, 82, 80],
    "Charlie": [92, 95, 93],
    "Diana": [65, 70, 68]
}

# Calculate statistics
for name, grades in students.items():
    average = sum(grades) / len(grades)
    max_grade = max(grades)
    min_grade = min(grades)

    print(f"\n{name}:")
    print(f"  Average: {average:.1f}")
    print(f"  Highest: {max_grade}")
    print(f"  Lowest: {min_grade}")

    # Determine grade letter
    if average >= 90:
        letter = "A"
    elif average >= 80:
        letter = "B"
    elif average >= 70:
        letter = "C"
    else:
        letter = "F"

    print(f"  Letter Grade: {letter}")
Expected Output:
Alice:
  Average: 87.7
  Highest: 90
  Lowest: 85
  Letter Grade: B

Bob:
  Average: 80.0
  Highest: 82
  Lowest: 78
  Letter Grade: B

Charlie:
  Average: 93.3
  Highest: 95
  Lowest: 92
  Letter Grade: A

Diana:
  Average: 67.7
  Highest: 70
  Lowest: 65
  Letter Grade: F

Key Takeaways

Conditionals

  • Use if/elif/else for decisions
  • Combine conditions with and, or, not
  • Nest conditionals for complex logic
  • Ternary operator for simple assignments

Loops

  • Use for for sequences
  • Use while for unknown iterations
  • range() generates number sequences
  • enumerate() gets index and value

Control Flow

  • break exits loop immediately
  • continue skips to next iteration
  • else with loops runs if no break

Best Practices

  • Prefer for over while when possible
  • Use enumerate() when you need index
  • Use .items() for dictionary loops
  • Avoid infinite loops in while
What's Next?

Excellent work! You now know how to control program flow with conditionals and loops. Next, we'll learn about functions, one of the most important concepts in programming:

  • Defining and calling functions - Create reusable blocks of code
  • Parameters and arguments - Pass data into your functions
  • Return values - Get results back from functions
  • Scope and variables - Understand local vs global scope
  • Lambda functions - Write compact, anonymous functions