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!

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

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

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

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")

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"

# Syntax: value_if_true if condition else value_if_false
max_value = a if a > b else b
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)
# Output:
# apple
# banana
# orange

# Loop through a string
for char in "Hello":
    print(char)
# Output: H, e, l, l, o (each on new line)

Using range()

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

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

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

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

Looping Through Lists

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

# Loop through values
for num in numbers:
    print(num * 2)
# Output: 2, 4, 6, 8, 10

# Loop with index using enumerate()
for index, value in enumerate(numbers):
    print(f"Index {index}: {value}")
# Output:
# Index 0: 1
# Index 1: 2
# Index 2: 3
# ...

# Modify list while looping (create new list)
squares = []
for num in numbers:
    squares.append(num ** 2)
# squares = [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])
# Output:
# name Alice
# age 30
# city New York

# 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}")
# Output:
# 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
# Output:
# 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.
# Discouraged: Triple nested for-loops
for a in A:
    for b in B:
        for c in C:
            # ...do something with combination of a, b, and c...
            pass

# Better: Use itertools.product to avoid nested loops.
from itertools import product

def process(a, b, c):
    # ...do something...
    pass

for a, b, c in product(A, B, C):
    process(a, b, c)

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!)
# Output: 1, 2, 3, 4, 5

# 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}")

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

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 loop immediately
    print(f"{num} is odd")
# Output:
# 1 is odd
# 3 is odd
# 5 is odd
# First even number: 8
# (Loop stops here, doesn't check 9 or 10)

# 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}")

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
    print(num)
# Output: 2, 4, 6, 8
# (1, 3, 5, 7 are skipped)

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

for num in numbers:
    if num < 0:
        continue
    print(f"Processing {num}")
# Only processes: 5, 8, 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"

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")

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}")

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}")

Sets

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

# Simple iteration (order not guaranteed)
for num in unique_numbers:
    print(num ** 2)

# Process only even numbers
for num in unique_numbers:
    if num % 2 == 0:
        print(f"{num} 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)}")

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:
        print(f"Congratulations! You guessed it in {attempts} attempts!")
        break
    elif guess < secret_number:
        print("Too low!")
    else:
        print("Too high!")

    remaining = max_attempts - attempts
    if remaining > 0:
        print(f"You have {remaining} attempts remaining.")
else:
    print(f"Game over! The number was {secret_number}.")

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}")

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