BASIC Python (Beginner Level) 📂 Basic Python Programming · 6 of 15 46 min read

Python Loops — for, while, break, continue & pass

Master Python loops from the ground up. This tutorial covers the for loop with animated flowcharts, the range() function, the while loop with condition-based iteration, when to choose for vs while, the break keyword to exit early, continue to skip iterations, pass as a do-nothing placeholder, the loop-else clause unique to Python, nested loops, four common loop patterns (accumulator, search, filter, map), common traps, and a full number-guessing game practice program.

Section 01

The Story That Explains Loops

The Assembly Line
Picture a car factory. A single worker doesn't build each car by hand from scratch. Instead, an assembly line moves — one car frame after another — through the same stations: install engine, attach doors, paint, test. The station doesn't care which car it's working on right now; it does the same task, over and over, on whatever arrives next. That's a loop.

Every program you'll ever write is packed with repetition: send an email to each customer, add up every number in a list, keep asking for a password until the user gets it right, play frame after frame until the game ends. Without loops, you'd have to write each step out by hand — an impossible way to build software. With loops, you write the task once, and tell Python to run it as many times as needed.

Python has two main loops — for (walk through a collection) and while (repeat while a condition holds) — plus three control keywords: break (jump out early), continue (skip to the next round), and pass (do literally nothing). Master these five and you've unlocked one of the most powerful tools in programming.
🔄
for
Iterate over a collection
"Do this for each item in that." Perfect when you know what you're walking through — a list, a range of numbers, characters in a string. Runs a fixed number of times.
while
Repeat until condition ends
"Keep doing this while some condition holds." Perfect when you don't know in advance how many rounds you'll need — waiting for input, retrying, simulating until stable.
🛑
break · continue · pass
Control the flow
Three keywords that fine-tune every loop. break exits immediately, continue skips to the next iteration, pass is a placeholder that does nothing.

Section 02

The for Loop — Walking Through a Collection

A for loop takes an iterable (something you can walk through, item by item) and executes its body once per item. The name of your loop variable — for x in ... — is bound to each element in turn.

The Flow of a for Loop

start any items left? (get next item) Yes execute body (with current item) go back for next item No done

Basic Syntax

# ─── LOOP OVER A LIST ───────────────────────────────────
fruits = ["apple", "banana", "cherry"]

for fruit in fruits:
    print(f"I like {fruit}")

# OUTPUT:
# I like apple
# I like banana
# I like cherry

# ─── LOOP OVER A STRING (characters) ────────────────────
for letter in "hello":
    print(letter)

# OUTPUT: h, e, l, l, o (each on its own line)

# ─── LOOP OVER A TUPLE ──────────────────────────────────
for item in ("red", "green", "blue"):
    print(item)

# ─── LOOP OVER A DICTIONARY (keys by default) ──────────
scores = {"Alice": 92, "Bob": 85}
for name in scores:
    print(f"{name}: {scores[name]}")

# ─── LOOP OVER A SET (order not guaranteed) ────────────
for n in {3, 1, 4, 1, 5}:
    print(n)
💡
Python's for Is a For-Each

Unlike C or Java, Python's for doesn't use a counter and condition. It's a for-each loop — it walks through the elements of an iterable directly. If you need an index, use enumerate(). If you need a counter, use range(). Never write for i in range(len(lst)): just to get at lst[i] — use for item in lst: instead.


Section 03

The range() Function — Your Numeric Companion

When you need to loop a specific number of times, or over a sequence of numbers, reach for range(). It generates numbers on demand — cheap, fast, and memory-efficient (even range(10_000_000) uses almost no memory).

One argument
range(stop)
0 up to (but not including) stop. range(5) → 0,1,2,3,4
Two arguments
range(start, stop)
start up to (but not including) stop. range(2, 6) → 2,3,4,5
Three arguments
range(start, stop, step)
Same, but jumping by step. range(0, 10, 2) → 0,2,4,6,8
# ─── COUNT FROM 0 TO N-1 ────────────────────────────────
for i in range(5):
    print(i)                    # 0, 1, 2, 3, 4

# ─── COUNT FROM A TO B-1 ────────────────────────────────
for i in range(1, 6):
    print(i)                    # 1, 2, 3, 4, 5

# ─── COUNT WITH A STEP ──────────────────────────────────
for i in range(0, 20, 3):
    print(i)                    # 0, 3, 6, 9, 12, 15, 18

# ─── COUNT DOWN (negative step) ─────────────────────────
for i in range(10, 0, -1):
    print(i)                    # 10, 9, 8, ..., 1

# ─── REPEAT SOMETHING N TIMES (don't need i) ───────────
for _ in range(3):
    print("Hello!")            # prints Hello! three times
🔑
Why Underscore? The Convention for "I Don't Care"

When you loop and don't need the loop variable, use _ (underscore). It's a Python convention meaning "I know I'm getting a value, but I'm ignoring it." Readers instantly understand your intent, and linters won't warn about unused variables.


Section 04

The while Loop — Repeat Until Condition Ends

A while loop repeats as long as a condition stays True. Before each iteration, Python checks the condition. If it's True, run the body; if it's False, exit the loop. Unlike for, you don't specify how many times — you specify when to stop.

The Flow of a while Loop

start condition True? (check first!) True execute body (MUST change condition!) re-check condition False done
# ─── COUNTDOWN ──────────────────────────────────────────
count = 5

while count > 0:
    print(f"T-minus {count}")
    count -= 1              # MUST modify — else infinite loop!
print("Liftoff!")

# OUTPUT:
# T-minus 5
# T-minus 4
# T-minus 3
# T-minus 2
# T-minus 1
# Liftoff!

# ─── INPUT VALIDATION (classic while use case) ──────────
password = ""
while len(password) < 8:
    password = input("Enter a password (8+ chars): ")
print("Password accepted")

# ─── SIMULATE UNTIL STABLE ──────────────────────────────
temperature = 100
while temperature > 25:
    temperature -= 5          # cool by 5 each step
    print(f"Now {temperature}°")
print("Cool enough.")
⚠️
The Infinite Loop Trap

If nothing in the body ever makes the condition False, the loop runs forever. Your program hangs; you have to force-kill it. Every while body MUST do something that eventually changes the condition — decrement a counter, update a state, break out on a signal. If you catch yourself with while True:, make sure there's a break inside.


Section 05

for vs while — When to Use Which

✅ Use for when...
SituationExample
You have a collection to walk throughfor item in list
You know the exact countfor i in range(10)
Processing each character of a stringfor c in text
Iterating dict keys/values/itemsfor k, v in d.items()
Fixed number of retriesfor _ in range(3)
⏳ Use while when...
SituationExample
Number of iterations is unknown up frontWait for user input
Loop depends on external stateRetry until server responds
Simulation until stablePhysics steps until at rest
Event loop / game loopwhile running:
Reading from a streamUntil EOF or sentinel value
💡
Rule of Thumb

If you can express your loop as "for each X in Y", use for. It's the safer choice — Python guarantees you'll finish because the iterable is finite. Reach for while only when the stopping condition truly depends on something you're computing or observing during the loop.


Section 06

break — Exit the Loop Immediately

break is the emergency exit. When Python hits a break, it jumps out of the enclosing loop right away — no more iterations, no more condition checks. Use it when you've found what you were looking for, or when something's gone wrong and there's no point continuing.

start more items? Yes break? Yes: BREAK! rest of body No exit break skips all remaining iterations and jumps straight out
# ─── SEARCH AND STOP ────────────────────────────────────
numbers = [4, 2, 7, 1, 9, 3, 6]

for n in numbers:
    if n == 9:
        print("Found 9!")
        break                     # stop looking — we're done
    print(f"Checking {n}...")

# OUTPUT:
# Checking 4...
# Checking 2...
# Checking 7...
# Checking 1...
# Found 9!

# ─── COMMON while True + break PATTERN ─────────────────
while True:
    answer = input("Continue? (yes/no): ")
    if answer.lower() == "no":
        break                     # exit the infinite loop
    print("Ok, once more...")
print("Goodbye!")

# ─── BREAK ONLY EXITS THE INNERMOST LOOP ────────────────
for i in range(3):
    for j in range(3):
        if j == 2:
            break                 # only breaks inner loop!
        print(i, j)
# OUTPUT:
# 0 0    0 1    1 0    1 1    2 0    2 1
# Outer loop still ran fully — break didn't escape it.

Section 07

continue — Skip to the Next Iteration

continue is the "skip this one" button. When Python hits it, the rest of the current iteration is skipped, and the loop moves on to the next item (or re-checks the condition, for a while loop).

start more items? Yes continue? Yes: CONTINUE (skip rest) rest of body No done continue skips the rest of THIS iteration, then goes back to check for more
# ─── SKIP NEGATIVE NUMBERS ──────────────────────────────
numbers = [5, -3, 8, -1, 4, -7, 2]

for n in numbers:
    if n < 0:
        continue                  # skip negatives entirely
    print(f"Processing {n}")

# OUTPUT:
# Processing 5
# Processing 8
# Processing 4
# Processing 2

# ─── FILTER + PROCESS PATTERN ──────────────────────────
words = ["apple", "", "  ", "banana", "cherry", ""]

for word in words:
    if not word.strip():        # empty or whitespace
        continue                  # skip garbage
    print(word.upper())

# OUTPUT: APPLE, BANANA, CHERRY

# ─── ONLY EVEN NUMBERS ─────────────────────────────────
total = 0
for n in range(1, 11):
    if n % 2 != 0:
        continue                  # skip odd numbers
    total += n
print(total)                     # 30 (2+4+6+8+10)
💡
break vs continue — The One-Line Distinction

break = "I'm done with the whole loop — get me out."
continue = "I'm done with THIS iteration — give me the next one."
Both are useful; both are readable when used sparingly. Overusing them turns a loop into a maze — reach for filter-first patterns (list comprehensions, guard clauses) when the logic gets tangled.


Section 08

pass — The "Do Nothing" Placeholder

pass is Python's no-op. It literally does nothing. Its whole reason to exist is that Python requires every code block to have at least one statement — you can't have an empty if, function, or class. pass is a legal placeholder that says "yes, there's supposed to be code here, but not yet."

# ─── PLACEHOLDER IN A FUNCTION YOU'LL WRITE LATER ───────
def send_email(recipient, message):
    pass                          # TODO: implement

# ─── PLACEHOLDER IN AN if BLOCK ─────────────────────────
if user.is_admin:
    pass                          # admins are allowed — do nothing special
else:
    log("non-admin access denied")
    raise PermissionError()

# ─── PLACEHOLDER IN A LOOP (skip processing) ────────────
for n in range(10):
    if n % 2 == 0:
        pass                      # explicitly «do nothing» for evens
    else:
        print(f"odd: {n}")

# ─── EMPTY CLASS DECLARATION ────────────────────────────
class Placeholder:
    pass                          # works — legal empty class

pass vs continue vs break — Not the Same Thing!

KeywordWhat It DoesEffect on the Loop
passNothing at allLoop continues normally — next statement runs
continueSkips rest of current iterationJumps back to check condition for next iteration
breakExits the loop entirelyLoop ends; execution moves past the loop
# ─── SEE THE DIFFERENCE IN ONE PROGRAM ──────────────────
for n in range(5):
    if n == 2:
        pass                      # no effect — just documentation
    print(n, end=" ")
# OUTPUT: 0 1 2 3 4   ← everything printed

print()
for n in range(5):
    if n == 2:
        continue                  # skip printing 2
    print(n, end=" ")
# OUTPUT: 0 1 3 4     ← 2 skipped

print()
for n in range(5):
    if n == 2:
        break                     # stop the loop
    print(n, end=" ")
# OUTPUT: 0 1         ← stopped at 2

Section 09

The else Clause — Python's Loop Superpower

Python loops have a feature almost no other language has: an else block that runs only if the loop finished normally (without hitting a break). It's perfect for the "search and confirm not found" pattern.

# ─── FIND AND REPORT MISSING ────────────────────────────
numbers = [1, 3, 5, 7, 9]
target = 4

for n in numbers:
    if n == target:
        print(f"Found {target}!")
        break
else:
    print(f"{target} not in the list")   # runs — no break happened

# OUTPUT: 4 not in the list

# ─── WITH break — else IS SKIPPED ──────────────────────
for n in numbers:
    if n == 5:
        print("Found 5!")
        break
else:
    print("Not found")              # does NOT run

# OUTPUT: Found 5!

# ─── while ALSO SUPPORTS else ──────────────────────────
i = 0
while i < 5:
    print(i)
    i += 1
else:
    print("Loop finished cleanly")   # runs — no break used
💡
Read it as "no break"

The else clause on a loop confuses many people because of its name. A better mental model: read it as "loop-else = no break happened". The block runs only when the loop completes its full iteration without an early exit. This makes it perfect for "we searched and didn't find it" messages.


Section 10

Nested Loops — Loops Inside Loops

You can put one loop inside another. The inner loop runs completely for each iteration of the outer loop. This is how you handle 2D grids, generate combinations, or process nested data.

# ─── MULTIPLICATION TABLE (3x3) ─────────────────────────
for i in range(1, 4):
    for j in range(1, 4):
        print(f"{i}x{j}={i*j:2d}", end="  ")
    print()                        # newline after each row

# OUTPUT:
# 1x1= 1  1x2= 2  1x3= 3
# 2x1= 2  2x2= 4  2x3= 6
# 3x1= 3  3x2= 6  3x3= 9

# ─── ITERATE OVER A 2D GRID ─────────────────────────────
grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

for row in grid:
    for cell in row:
        print(cell, end=" ")
    print()

# ─── BREAKING OUT OF NESTED LOOPS ──────────────────────
# break exits ONLY the innermost loop
found = False
for i in range(3):
    for j in range(3):
        if i * j >= 4:
            found = True
            break                 # breaks inner only
    if found:
        break                     # check & break outer manually
print(f"Stopped at ({i}, {j})")
⚠️
Nested Loops Explode Quickly

Two nested loops over 1000 items each is 1,000,000 operations. Three loops is a billion. Before nesting, ask if there's a smarter way — a set for membership tests, a dict lookup, or a comprehension. If you truly need nesting, keep the inner range small and the operations cheap.


Section 11

Common Loop Patterns You'll Use Forever

💰
Accumulator
Build up a total/list/string as you loop. Start with an "empty" value, add to it each iteration. Foundation of sum, count, join, and many algorithms.
running total
🔍
Search
Walk until you find what you're looking for. Use break to stop early. Pair with for-else to report "not found".
find and stop
📋
Filter
Keep only items that match a condition. Use continue for the skip-and-move-on version, or a list comprehension for the compact version.
select subset
🔄
Map/Transform
Apply the same operation to every item and collect results. Common enough that Python has a built-in — list comprehensions almost always read better than an explicit loop.
reshape each item
# ─── ACCUMULATOR: sum a list ───────────────────────────
numbers = [10, 20, 30, 40]
total = 0                        # start empty
for n in numbers:
    total += n                    # accumulate
print(total)                    # 100

# ─── ACCUMULATOR: build a string ───────────────────────
words = ["Python", "is", "fun"]
sentence = ""
for w in words:
    sentence += w + " "
print(sentence.strip())          # Python is fun

# ─── SEARCH: find first even number ────────────────────
nums = [7, 3, 9, 12, 15]
for n in nums:
    if n % 2 == 0:
        print(f"First even: {n}")
        break
else:
    print("No even numbers")

# ─── FILTER: keep only positives ────────────────────────
data = [3, -1, 7, -4, 2, 0]
positives = []
for n in data:
    if n > 0:
        positives.append(n)
print(positives)                # [3, 7, 2]

# Same thing as a comprehension (Pythonic!)
positives = [n for n in data if n > 0]

# ─── MAP: transform each value ─────────────────────────
prices = [10.0, 20.0, 30.0]
with_tax = []
for p in prices:
    with_tax.append(p * 1.15)
print(with_tax)                 # [11.5, 23.0, 34.5]

# Comprehension version
with_tax = [p * 1.15 for p in prices]

Section 12

Common Traps and How to Avoid Them

The infinite while loop
Forgetting to change the condition inside the body. while x > 0: print(x) runs forever if you never decrement x. Always verify the condition can eventually become False.
Off-by-one with range()
range(5) gives 0..4, not 0..5. If you want 1 through 5, use range(1, 6). Always remember: stop is exclusive.
Mutating a list while looping
Removing items from a list you're iterating skips elements and causes bugs. Iterate over a copy with for x in lst[:]: or build a new list with a comprehension.
Modifying dict size during iteration
Adding or deleting keys mid-loop raises RuntimeError. Loop over list(d.keys()) if you must, or build a new dict.
Using the loop variable after the loop
for i in range(5): pass; print(i) prints 4 — the last value persists. Not always a bug, but relying on it is fragile. If needed, save the value explicitly before the loop ends.
break in the wrong loop
break only exits the innermost loop. To break out of nested loops, use a flag variable or refactor into a function that uses return.

Section 13

Complete Practice Program — Number Guessing Game

# guessing_game.py — every loop concept in one program

import random

def get_valid_guess(low, high):
    """Loop until user enters a valid number (uses while + break)."""
    while True:                       # infinite loop with break exit
        raw = input(f"Guess ({low}-{high}): ")
        if not raw.strip():
            print("  Empty input — try again.")
            continue               # skip rest, ask again
        try:
            n = int(raw)
        except ValueError:
            print("  Not a number — try again.")
            continue
        if not low <= n <= high:
            print(f"  Out of range — must be {low}-{high}.")
            continue
        return n                     # valid — break out via return

def play_round(low, high, max_tries):
    """One round using a for loop with break and for-else."""
    secret = random.randint(low, high)
    print(f"\nI'm thinking of a number between {low} and {high}.")
    print(f"You have {max_tries} guesses.\n")

    for attempt in range(1, max_tries + 1):
        guess = get_valid_guess(low, high)

        if guess == secret:
            print(f"🎉  Correct! You got it in {attempt} tries.")
            return True
        elif guess < secret:
            print(f"  Too low.  ({max_tries - attempt} guesses left)\n")
        else:
            print(f"  Too high. ({max_tries - attempt} guesses left)\n")
    else:                             # for-else: only runs if no break/return
        print(f"😕  Out of tries. The number was {secret}.")
        return False

def main():
    # Accumulator: track wins and losses
    wins, losses = 0, 0

    while True:
        won = play_round(low=1, high=20, max_tries=5)
        if won:
            wins += 1
        else:
            losses += 1

        again = input("\nPlay again? (y/n): ").strip().lower()
        if again != "y":
            break                     # exit the game loop

    print(f"\nFinal score: {wins} wins, {losses} losses.")

if __name__ == "__main__":
    main()
EXAMPLE SESSION
I'm thinking of a number between 1 and 20. You have 5 guesses. Guess (1-20): 10 Too low. (4 guesses left) Guess (1-20): 15 Too high. (3 guesses left) Guess (1-20): abc Not a number — try again. Guess (1-20): 13 Too low. (2 guesses left) Guess (1-20): 14 🎉 Correct! You got it in 4 tries. Play again? (y/n): n Final score: 1 wins, 0 losses.
🎯
Every Concept, One Program

This one program uses: while True with break (input validation), continue (skip invalid input), for with range (guess attempts), for-else (out-of-tries message), an accumulator (score tracking), and a return to exit both a function and its enclosing loop. Read it a few times — every pattern in this tutorial is in there.


Section 14

Golden Rules

🐍 Python Loops — Non-Negotiable Rules
1
Prefer for when you can, while when you must. A for loop over an iterable cannot loop forever. A while loop can — so use it only when the stopping condition genuinely depends on runtime state.
2
Every while body must change the condition. If the condition can never become False, you have an infinite loop. Trace through your loop by hand for a few iterations to be sure.
3
range(stop) is exclusive at the top. range(5) gives 0, 1, 2, 3, 4 — five values, not six. When in doubt, run the range through list() in a REPL to see what you get.
4
Never modify a list or dict while iterating over it. Removing items shifts indices; adding items in a dict raises RuntimeError. Either build a new collection with a comprehension, or iterate over a shallow copy.
5
break stops the whole loop; continue stops just this iteration; pass does nothing at all. Three keywords, three completely different jobs. Confusing them creates baffling bugs.
6
break only exits the innermost loop. To bail out of nested loops, use a flag, refactor into a function and return, or restructure the logic. Python has no break 2.
7
Loop else means "no break happened". That's the useful mental model. Perfect for the "searched and didn't find it" pattern. Skip else entirely if the loop always uses break — it becomes dead code.
8
Reach for comprehensions when your loop just filters or transforms. [n*2 for n in nums if n > 0] is faster and clearer than the equivalent 4-line loop. Save explicit loops for when logic is complex or has side effects.