🐍 Python Course · Stage 4 · Lesson 63 of 89 · Concept: Loops
Course Overview →

🟩 These concept pages connect Python to programming in general. Read Beginner tab first.

← Concept: Control Flow Concept: Functions →
thecodex.expert  ·  The Codex Family of Knowledge
CS Concepts

Loops

How programs repeat — counting through numbers, iterating collections, or running until a condition changes.

CS Concepts Requires: Variables ~16 min read Last verified:
Canonical Definition

A loop is a control flow construct that causes a sequence of statements to execute repeatedly, either for a fixed number of iterations (for loop), while a condition holds true (while loop), or over each element of a collection (for-each / range-based loop), with the ability to exit early (break) or skip to the next iteration (continue).

🟩 Beginner

Repeating actions — the engine of programs

What you'll learn: for loops (repeat for each item in a collection), while loops (repeat until a condition is false), break/continue, and how loops differ across languages.

How to read this tab: The Python-specific loop syntax is covered at Control Flow & Loops. This page explains why loops exist and how the same concept appears in every language.

⏱ 20 min 📄 4 sections 🔶 Prerequisite: Control Flow
One sentence that captures it

A loop is a set of instructions written once that the computer follows multiple times — like a recipe step that says "repeat until done."

Python's for is a for-each, not a counting loop. C's for (int i=0; i<n; i++) is a counting loop. Python's for item in collection iterates over any iterable directly. To count in Python, use range(): for i in range(n). This is more readable and less error-prone — no off-by-one errors from < vs <=.

The for loop: repeat a fixed number of times

A for loop repeats a block of code a specific number of times, or once for each element in a sequence. In Python, for i in range(5) runs the loop body 5 times, with i taking values 0, 1, 2, 3, 4.

Pythonloops.py
# Count from 0 to 4
for i in range(5):
    print(i)  # 0, 1, 2, 3, 4

# Iterate over a list
fruits = ["mango", "apple", "banana"]
for fruit in fruits:
    print(fruit)  # mango, apple, banana

# C-style for loop equivalent in Python using range
for i in range(0, 10, 2):   # start=0, stop=10, step=2
    print(i)  # 0, 2, 4, 6, 8

While loops risk infinite loops. A for loop terminates when the collection runs out. A while loop only terminates if the condition eventually becomes False. Always verify that something inside the loop modifies the condition. The classic infinite loop: while True: — only safe with a break statement inside.

The while loop: repeat until a condition changes

A while loop runs as long as a condition is true. Use it when you don't know in advance how many times you'll need to repeat — for example, keep asking for input until the user enters something valid.

Pythonwhile_loop.py
total = 0
number = 1
while number <= 100:        # repeat while true
    total += number
    number += 1             # update: without this, infinite loop!
print(total)                # 5050 — sum of 1 to 100
💡

For-each is the safest loop. It can't go out of bounds (no index arithmetic). It works on any iterable — lists, strings, files, generators, dict keys. When you need the index too, use enumerate(): for i, item in enumerate(collection). When you need to iterate two sequences together, use zip(): for a, b in zip(list1, list2).

For-each: iterating collections

Modern languages provide a clean syntax for iterating every element of a collection without managing an index counter. Python's for item in collection is idiomatic. Java has for (Item item : collection). JavaScript has for (const item of array). This is the preferred loop in most cases when you need every element — it's simpler and harder to get wrong than index-based loops.

💡

Python's loop else is unusual and powerful. A for/while loop can have an else clause that runs only if the loop completed without hitting break. This is perfect for "search and not found" patterns: for item in collection: if matches(item): found = item; break; else: found = None. The else means "the break was never hit."

Break and continue

break exits the loop immediately, regardless of whether the condition is still true. continue skips the rest of the current iteration and jumps to the next one.

Pythonbreak_continue.py
numbers = [3, 7, 2, 9, 1, 5]

# break: stop when we find 9
for n in numbers:
    if n == 9:
        print("Found 9!")
        break
    print(n)   # 3, 7, 2, then stops

# continue: skip even numbers
for n in range(10):
    if n % 2 == 0:
        continue      # skip even numbers
    print(n)          # 1, 3, 5, 7, 9
🔵 Intermediate

Loop patterns, Big O, enumerate, and zip

What you'll learn: Common loop patterns and when to use each. How nested loops affect time complexity (Big O). Python's enumerate() and zip() for cleaner loop code.

How to read this tab: The Big O section connects to algorithm complexity — essential background for technical interviews and understanding why some code is slower than others.

⏱ 20 min 📄 3 sections 🔶 Prerequisite: Beginner tab
📎

The same loop ideas appear in every language — syntax only varies. Python's for x in list is JavaScript's for (const x of list) is Java's for (Type x : list) is Go's for _, x := range list. Learn the concept once; the syntax adaptation is trivial.

Loop patterns across languages

Multi-languageloops_comparison.txt
C (index-based for):
  for (int i = 0; i < 10; i++) { printf("%d ", i); }

Java (for-each):
  for (String s : list) { System.out.println(s); }

JavaScript (for...of):
  for (const item of array) { console.log(item); }

Rust (range):
  for i in 0..10 { println!("{}", i); }

Go:
  for i := 0; i < 10; i++ { fmt.Println(i) }  // C-style
  for _, v := range slice { fmt.Println(v) }   // range-based

Python do-while equivalent (Python has no do-while):
  while True:
      data = input("Enter value: ")
      if data: break

Nested loops multiply complexity. One loop over n items is O(n). Two nested loops are O(n²). Three nested loops are O(n³). This matters enormously for performance: O(n²) with n=10,000 is 100 million operations. Before writing a nested loop, ask: is there a data structure (dict/set) that solves this in O(n) instead?

Nested loops and Big O

Loops are the primary source of algorithmic complexity. A single loop over n elements is O(n). Two nested loops over n elements is O(n²). Three levels of nesting is O(n³). Understanding loop complexity is the foundation of algorithm analysis — recognising when a nested loop can be replaced by a more efficient approach (sorting, hashing, binary search) is a core programming skill.

Pythoncomplexity.py
n = 1000

# O(n) — one loop
for i in range(n):
    pass  # executes n times

# O(n²) — nested loop
for i in range(n):
    for j in range(n):
        pass  # executes n*n = 1,000,000 times

# O(n log n) — halving inner range
for i in range(n):
    j = n
    while j > 0:
        j //= 2   # executes n * log₂(n) ≈ 10,000 times
💡

enumerate() and zip() make Python loops more readable and less error-prone. Never write for i in range(len(items)): item = items[i] — write for i, item in enumerate(items). zip() stops at the shorter sequence — use itertools.zip_longest() if you need to handle unequal lengths.

Enumerate and zip

Two built-in patterns eliminate the need for manual index counters in Python: enumerate(iterable) yields (index, value) pairs — use when you need both the index and the value. zip(a, b) iterates two sequences in parallel — use when you need corresponding elements from multiple collections. Both are more readable and less error-prone than index-based loops.

Commonly confused
A for-each loop and a for loop are not the same construct. A C-style for (int i = 0; i < n; i++) is an index-based counting loop. A for-each loop (for item in collection) iterates a collection without an index. Modifying a collection's length while iterating it with a for-each loop is undefined in some languages (ConcurrentModificationException in Java) and silently wrong in others.
A while loop with no exit condition is an infinite loop — not always a mistake. Event loops in servers, game loops, and operating system process schedulers are intentional infinite loops (while True:) that wait for and process events. The break statement inside the loop provides the controlled exit.
break only exits the innermost loop. In a nested loop, break exits only the loop that directly contains it. To exit an outer loop from inside an inner loop in Python, use a flag variable or restructure as a function with return. Some languages (Java, Kotlin) support labelled breaks to exit a specific outer loop.
🔴 Expert

Loop invariants, loop unrolling, and vectorisation

What you'll learn: Loop invariants for formally proving loop correctness. How compilers optimise loops (unrolling, vectorisation with SIMD instructions). Why NumPy's vectorised operations beat Python loops.

How to read this tab: Read when studying algorithms formally or when optimising numerical code.

⏱ 20 min 📄 2 sections 🔶 Prerequisite: After Stage 2

Loop invariants and formal correctness

A loop invariant is a condition that holds true at the beginning of every iteration of a loop. Establishing, maintaining, and using a loop invariant is the formal technique for proving that a loop is correct. To prove a loop correct: (1) show the invariant holds before the loop begins (initialisation); (2) show that if it holds at the start of an iteration, it still holds at the end (maintenance); (3) show that when the loop exits, the invariant plus the exit condition gives the desired result (termination). Dijkstra and Hoare formalised this in the 1970s. It is the basis of formal verification and the conceptual foundation of why loop invariants matter in practice.

Loop unrolling and vectorisation

Loop unrolling is a compiler optimisation that replicates the loop body multiple times to reduce loop overhead (branch instruction, counter update, condition check). GCC and Clang apply this at -O2 and above. Vectorisation transforms scalar loops into SIMD (Single Instruction, Multiple Data) instructions that process multiple elements per clock cycle. A loop summing 1000 floats can be vectorised to process 8 floats at a time using AVX2 instructions, reducing 1000 iterations to ~125. Controlled by -O3 and -march=native in GCC/Clang.

Specification reference

Knuth, D. E. (1997). The Art of Computer Programming, Vol. 1: Fundamental Algorithms. Addison-Wesley. — Formal loop analysis and algorithmic complexity. Dijkstra, E. W. (1976). A Discipline of Programming. Prentice Hall. — Loop invariants and formal program correctness.

How this connects
Requires first
Source confidence: High Last verified: Primary source: Knuth, D. E. (1997). The Art of Computer Programming, Vol. 1. Addison-Wesley.

✅ Expert tab complete

  • I can explain what a loop invariant is and why it's useful for proving correctness
  • I know why a NumPy vectorised operation is ~100x faster than an equivalent Python loop

Continue to Functions →

Sources

1
Knuth, D. E. (1997). The Art of Computer Programming, Vol. 1: Fundamental Algorithms (3rd ed.). Addison-Wesley. — Formal loop analysis, loop invariants, and algorithmic complexity.
2
Dijkstra, E. W. (1976). A Discipline of Programming. Prentice Hall. — Loop invariants and formal correctness proofs.
3
Python Software Foundation. The Python Language Reference, §8 — Compound statements: for, while, break, continue. docs.python.org/3/reference/compound_stmts.html.
4
ISO/IEC 9899:2018 (ISO C17). §6.8.5 — Iteration statements. International Organization for Standardization.