Loops let your program repeat work. Python offers the for loop for iterating over iterables, the while loop for repeating while a condition holds, and the loop-else clause that runs only when the loop completes without break. This lesson walks through every form with real examples.
The for loop over iterables
A Python for loop walks through any iterable one item at a time. Lists, tuples, dicts, sets, strings, files, generators, and any custom object with __iter__ are all valid. The loop variable takes each value in turn.
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# apple
# banana
# cherry
# Iterate over a string character by character
for ch in "Python":
print(ch, end=" ")
# P y t h o n
range and the numeric for loop
Use range to drive a numeric loop. range(n) is 0 to n-1. range(start, stop) is start to stop-1. range(start, stop, step) adds a stride. range is lazy and cheap, even for huge ranges.
for i in range(5):
print(i, end=" ") # 0 1 2 3 4
print()
for i in range(2, 11, 2):
print(i, end=" ") # 2 4 6 8 10
print()
# Walk a list with index using enumerate
items = ["a", "b", "c"]
for index, value in enumerate(items, start=1):
print(f"{index}. {value}")
# 1. a
# 2. b
# 3. c
The while loop
while repeats as long as a condition is true. Make sure the condition eventually becomes false, or you get an infinite loop. The break statement exits the loop early. The continue statement skips the rest of the body and re-tests the condition.
n = 1
while n <= 5:
print(n)
n += 1
# 1 2 3 4 5
# break exits the loop
total = 0
while True:
s = input("> ").strip()
if s == "done":
break
total += int(s)
print("total:", total)
The loop-else clause
Python loops can have an else block. The else runs only if the loop finished normally, not via break. This is unusual but useful for search loops where you want a "not found" path.
def find(numbers, target):
for n in numbers:
if n == target:
print(f"found {target}")
break
else:
print(f"{target} not in list")
find([1, 2, 3, 4, 5], 3) # found 3
find([1, 2, 3, 4, 5], 99) # 99 not in list
Iterating over dicts
Iterating a dict gives you its keys. Use .items() to get key and value together, and .values() or .keys() when you only need one. Insertion order is preserved in Python 3.7+.
user = {"name": "Asha", "role": "admin", "age": 28}
for key in user:
print(key) # name role age
for key, value in user.items():
print(f"{key} = {value}") # name = Asha, role = admin, age = 28
Comprehensions as loop expressions
List, dict, and set comprehensions are loop expressions that build a new collection in one line. Use them when a loop exists only to create a new list. Avoid them when the body gets complex or has side effects.
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
squares = [n * n for n in nums if n % 2 == 0]
print(squares) # [4, 16, 36, 64, 100]
# Dict comprehension
word_lengths = {w: len(w) for w in ["apple", "banana", "cherry"]}
print(word_lengths) # {'apple': 5, 'banana': 6, 'cherry': 6}
Common pitfalls in loops
Three traps catch almost every Python programmer at some point. The first is mutating a list while iterating over it. Writing for item in items: items.remove(item) skips elements and may produce IndexError, because the iterator's index advances while the list shrinks. The fix is either iterate over a copy (for item in items[:]:) or build a new list with a comprehension. The second trap is the classic off-by-one with range. range(10) gives you 0..9, not 1..10; remember that stop is exclusive. The third is the infinite while loop. If the loop body never advances the condition toward false, the program hangs. As a defensive habit, write the loop with a hard upper bound and break out if it exceeds that limit, especially in code that may run unattended on a server.
How Python loops compare to JavaScript and C++
If you have written JavaScript or C++ loops, Python's for will look different and that is intentional. JavaScript has three for shapes: classic C-style for (let i = 0; i < n; i++), for-of over iterables, and for-in over object keys. Python collapses those into one for loop that always means "walk this iterable". C++ offers the classic index loop and the range-based for (for (auto& item : vec) { ... }), which is closest to Python's for. The key win is that Python never exposes the index by default, so you cannot accidentally introduce off-by-one errors unless you ask for it via range or enumerate. The mental model to keep: Python's for iterates a protocol, not a numeric counter.
Performance notes for large datasets
When a loop processes hundreds of thousands of items, small choices compound. Avoid calling len() inside the loop body when it does not change; hoist it before the loop. Prefer local references: inside the body, write local_append = result.append and call local_append(item) instead of result.append(item) for a measurable speedup on tight loops. For numerical work on arrays, drop down to NumPy or Pandas where a single vectorised expression replaces thousands of Python iterations. And remember that comprehensions are usually faster than an explicit for with append because they skip the attribute lookup and method call on each iteration.
Summary
Use for when you have an iterable, while when you have a condition, and break or continue to control flow inside. The next lesson covers lists, tuples, and the core sequence types you will use every day.