SythraOpen app

More Tools for Iteration in Python

Beyond while and for, Python's iteration toolkit includes continue (skip a round), pass (do-nothing placeholder), a loop else clause (runs only without break), enumerate() and zip() for pairing data, list comprehensions for compact loops, and generators for producing values on demand.

Sythra

12 min read

XLinkedIn
More Tools for Iteration in Python — cover illustration

You've got the core loops down — while, for, and the do-while pattern with nesting. This guide rounds out your iteration toolkit: finer control with continue and pass, the surprising else clause on loops, and the everyday helpers enumerate(), zip(), and list comprehensions — plus a peek at what's actually happening under the hood.

What you will learn

  • continue — skip just the current pass, without leaving the loop
  • pass — a placeholder that does nothing, for code you haven't written yet
  • The else clause on loops — runs only if break never fired
  • enumerate() — get the index and value together
  • zip() — loop over two lists side by side
  • List comprehensions — build a list in one line
  • What an iterator actually is, and a first look at generators

continue: skipping just this one round

You already know break, which exits a loop completely and immediately. continue is its gentler cousin — instead of leaving the loop entirely, it just skips the rest of the current pass and jumps straight back to the top, to check the condition and start the next round.

for i in range(1, 6):
    if i == 3:
        continue
    print(i)

# Output:
# 1
# 2
# 4
# 5

Notice 3 is missing. When i becomes 3, Python hits continue, immediately abandons the rest of that pass (so print(i) never runs for 3), and jumps straight back up to grab the next value from range(). The loop itself keeps going — it never exits.

A very common real use is skipping invalid or unwanted items while processing a list:

numbers = [4, -1, 7, -3, 9]

for n in numbers:
    if n < 0:
        continue
    print(n, "squared is", n ** 2)

# Output:
# 4 squared is 16
# 7 squared is 49
# 9 squared is 81

pass: the "do nothing" placeholder

Sometimes you need to write the structure of some code — a loop, an if, a function — before you've actually figured out what should go inside it. Python won't let you leave a block completely empty; it expects at least one line of indented code underneath. That's exactly what pass is for: a statement that does absolutely nothing, purely to keep your code valid while you figure out the rest later.

for i in range(10):
    if i % 2 == 0:
        pass    # TODO: handle even numbers later
    else:
        print(i, "is odd")

Here, you've decided what should eventually happen for even numbers, but you haven't written it yet. pass lets the program run correctly right now, as a placeholder, without Python throwing an error about an empty block. You'll often see pass paired with a # TODO comment, exactly like above.

The else clause on loops

This one genuinely surprises almost every Python learner the first time they see it: both for and while loops can have their very own else: block attached. It has nothing to do with if/else — it's a completely separate idea that just happens to reuse the same word.

for i in range(1, 6):
    if i == 3:
        break
    print(i)
else:
    print("Loop finished without a break!")

# Output:
# 1
# 2

Notice the else: message is completely missing this time. Because break fired (when i reached 3), the loop did not finish naturally — so Python skips the else: block entirely. The classic real-world use is searching for something:

numbers = [2, 4, 6, 8, 10]

for n in numbers:
    if n == 7:
        print("Found 7!")
        break
else:
    print("7 was not found in the list.")

# Output:
# 7 was not found in the list.

This pattern — loop, with a break for "found it," and an else: for "never found it" — is the single most common reason Python programmers reach for this feature. while loops use the exact same rule.

enumerate(): getting the index and the value together

When you loop over a list with a plain for loop, you only get the value of each item — not its position. The clumsy way to get the position is a hand-tracked counter:

fruits = ["apple", "banana", "mango"]
index = 0
for fruit in fruits:
    print(index, fruit)
    index += 1

Python gives you a much cleaner built-in tool for exactly this job:

fruits = ["apple", "banana", "mango"]
for index, fruit in enumerate(fruits):
    print(index, fruit)

# Output:
# 0 apple
# 1 banana
# 2 mango

Notice the position numbering starts at 0, which is standard in Python. If you want it to start counting from 1, enumerate() lets you say so directly: enumerate(fruits, start=1).

zip(): looping over two lists side by side

Sometimes you have two related lists — like names and matching ages — and you want to loop over both together, pairing up the items that belong together.

names = ["Asha", "Ravi", "Mei"]
ages = [25, 31, 28]

for name, age in zip(names, ages):
    print(name, "is", age, "years old")

# Output:
# Asha is 25 years old
# Ravi is 31 years old
# Mei is 28 years old

zip() walks through both lists at exactly the same pace, pairing up the first item of names with the first item of ages, then the second with the second, and so on — like a zipper joining two rows of teeth together.

Looping over a dictionary directly with a plain for only gives you the keys. Use .items() to get both key and value together on each pass: for name, age in ages.items():.

List comprehensions: a loop that builds a list in one line

Once you're comfortable with for loops, you'll start noticing a very common pattern: looping over something, and building up a brand-new list as you go.

squares = []
for n in range(1, 6):
    squares.append(n ** 2)

print(squares)
# [1, 4, 9, 16, 25]

Python offers a much more compact way to write exactly this same pattern, called a list comprehension:

squares = [n ** 2 for n in range(1, 6)]
print(squares)
# [1, 4, 9, 16, 25]

Read it almost like English, rearranged: "Give me n ** 2, for every n in range(1, 6)." You can even add a condition, to only include certain items:

even_squares = [n ** 2 for n in range(1, 11) if n % 2 == 0]
print(even_squares)
# [4, 16, 36, 64, 100]

List comprehensions are extremely common in real Python code because they pack a loop, a transformation, and an optional filter all into one short, readable line — but if a comprehension ever starts feeling cramped or hard to read, there's nothing wrong with just writing the longer, regular loop instead. Readability always wins.

What's really happening underneath: iterators

When you write for fruit in fruits:, how does Python actually know how to "go through" the list, one item at a time? The answer is a concept called an iterator.

Anything you can loop over with for — a list, a string, a range, a dictionary — is called an iterable. Behind the scenes, Python calls a built-in function, iter(), on that iterable, which produces an iterator — a special object that knows how to hand out one item at a time, in order, whenever asked. Python then repeatedly calls next() on that iterator, to fetch each item, until there are none left.

fruits = ["apple", "banana", "mango"]
iterator = iter(fruits)

print(next(iterator))    # apple
print(next(iterator))    # banana
print(next(iterator))    # mango
print(next(iterator))    # raises StopIteration — nothing left!

The very last line raises an exception called StopIteration — Python's internal way of saying "there's nothing left to give you." This is exactly the signal a for loop is secretly watching for the whole time — it keeps calling next() automatically, and the moment it catches a StopIteration, it quietly stops the loop, without ever showing you that exception.

Generators: producing values one at a time, on demand

A generator is a special kind of function that produces a whole sequence of values, one at a time, instead of building and returning the entire list all at once. You create one almost exactly like a normal function, except you use the keyword yield instead of return:

def countdown(n):
    while n > 0:
        yield n
        n -= 1

for number in countdown(3):
    print(number)

# Output:
# 3
# 2
# 1

Calling countdown(3) doesn't actually run any of the code inside yet — it just hands you back a special generator object. Each time through the loop, the function runs until it hits yield, hands out that one value, and then pauses itself completely, remembering exactly where it left off.

You don't need to master generators right now — they're a genuinely more advanced topic. For now, just remember: yield means "hand out one value, then pause here until asked for the next one," which is a very different idea from return, which ends the function completely and forever.

Common mistakes

  • Confusing continue (skip this round) with break (exit the loop entirely)
  • Expecting a loop's else: to behave like an if/else — it only runs when break never fired
  • Looping over a dictionary and expecting values, when a plain for only yields keys
  • Writing an overly dense list comprehension that's harder to read than the equivalent loop

Common questions

What is the difference between break and continue in Python?

break exits the loop entirely, skipping any remaining iterations. continue only skips the rest of the current pass and moves on to the next iteration — the loop itself keeps running.

What does pass do in Python?

pass is a statement that does nothing. It's used as a placeholder wherever Python's syntax requires an indented block of code, but you haven't written the actual logic yet.

When does a loop's else clause run?

A for or while loop's else: block runs only if the loop finishes naturally, without ever hitting a break statement. If break fires, the else block is skipped entirely.

What does enumerate() do in Python?

enumerate() wraps an iterable and yields pairs of (index, value) on each pass of a for loop, so you get the position and the item together without manually tracking a counter.

What is the difference between a list and a generator in Python?

A list stores every value in memory at once. A generator (created with a function using yield) produces values one at a time, on demand, pausing between each — making it far more memory-efficient for large or unbounded sequences.

Explore

Related topics

Keep going — these sit next to this concept in a real learning path.

Browse all python explainers →