for
Python’s only counting-free loop: it walks an iterable item by item — and its optional else runs only when no break happened.
for
for target in iterable: body
for / else
for target in iterable: body # may break else: no_break # only if no break
Use for
for x in items / for i, x in enumerate(items) / for k, v in d.items()
Result
a statement — no value; target stays bound to the last item
Pairs with
break, continue, else, range(), enumerate(), zip()
Watch out
else means "no break", not "empty"; never mutate the list you loop over
Demo
Live evaluation
Add up a list. The loop variable survives the loop — unless the list was empty.
Try:
Inputs
numberslistcomma-separated
Code
total = 0 for n in [3, 5, 8]: total += n (total, n)
Result
(16, 8)
Two things to notice. In the for tab, n is still readable after the loop (it holds the last item) — but with an empty list the body never ran, so n was never bound and the last line fails. In the for / else tab, else is not "if empty": an empty list reaches else too, because nothing broke out of the loop.
Syntax slots
| Name | Type | Required | Description |
|---|---|---|---|
| target | name / pattern | yes | Assigned each item in turn. Can unpack: for i, x in … . Still bound to the last item after the loop. |
| iterable | expression | yes | Evaluated once, before the first iteration. Anything with __iter__ (or __getitem__). |
| body | block | yes | Runs once per item. break leaves the loop, continue skips to the next item. |
| else | block | no | Runs once after the last item — skipped if the loop was left with break (or return / an exception). |
Common patterns
Index and item together
enumerate instead of range(len(...)).
for i, line in enumerate(lines, start=1): print(i, line)
Two sequences in step
zip stops at the shortest; strict=True (3.10+) raises on a length mismatch instead.
for name, score in zip(names, scores, strict=True): print(name, score)
Dict keys and values
Looping a dict gives keys; .items() gives both.
for key, value in config.items(): print(key, "=", value)
Search with a fallback
for / else replaces a found-flag variable.
for user in users: if user.is_admin: break else: user = None
Examples
1. Loop over a list
for fruit in ['apple', 'pear']:
print(fruit)
Returns
apple
pear2. range() counts
for i in range(3):
print(i)
Returns
0
1
23. Strings iterate by character
for ch in 'hi!':
print(ch)
Returns
h
i
!4. Dicts iterate by key
for k in {'a': 1, 'b': 2}:
print(k)
Returns
a
b5. Unpacking in the target
for name, age in [('Ann', 31), ('Bo', 25)]:
print(name, age)
Returns
Ann 31
Bo 256. The target outlives the loop
for i in range(3):
pass
i
Returns
27. else runs when nothing broke
for x in [1, 3, 5]:
if x % 2 == 0:
break
else:
print('no even number')
Returns
no even numberPitfalls
1. Removing items from the list you are looping over
The loop walks by index; deleting shifts the remaining items left, so the one after each removal is skipped.
remove while looping
nums = [1, 2, 2, 3] for n in nums: if n == 2: nums.remove(n) nums
[1, 2, 3]
build a new list
nums = [1, 2, 2, 3] nums = [n for n in nums if n != 2] nums
[1, 3]
2. Reading else as "if the loop was empty"
else runs whenever the loop was not left with break — including after a normal full pass.
expects else to be skipped
for x in [1, 2]: pass else: print('empty?')
empty?
test emptiness directly
items = [1, 2] if not items: print('empty') else: print(len(items), 'items')
2 items
3. Closures capture the variable, not the value
Functions created in a loop all see the loop variable’s final value. Bind the current value as a default argument.
late binding
funcs = [] for i in range(3): funcs.append(lambda: i) [f() for f in funcs]
[2, 2, 2]
default argument
funcs = [] for i in range(3): funcs.append(lambda i=i: i) [f() for f in funcs]
[0, 1, 2]
4. Changing a dict’s size while looping over it
Adding or deleting keys during iteration raises. Loop over a snapshot of the keys instead.
delete during loop
d = {'a': 1, 'b': 0} for k in d: if d[k] == 0: del d[k]
RuntimeError: dictionary changed size during iteration
loop over list(d)
d = {'a': 1, 'b': 0} for k in list(d): if d[k] == 0: del d[k] d
{'a': 1}
When to use
Use it
- Doing something with every item of a collection, file or generator
- Searching with an early exit (break) and a not-found branch (else)
- Counting a fixed number of times with range()
Reach for something else
- Building a new list from an old one → a list comprehension
- Looping until a condition changes, not over items → while
- Summing / finding max / checking any → sum(), max(), any() directly
Notes
CPython impl
Compiles to GET_ITER once, then FOR_ITER per step; the loop ends when the iterator raises StopIteration
Scope
for does not create a scope — the target is an ordinary variable of the enclosing function or module
Iterable
The iterable expression is evaluated once; reassigning the name inside the loop does not change what is looped over
FAQ
It runs once after the loop finishes normally — that is, when the loop was not exited with break (or return, or an exception). It is meant for search loops: break when you find something, handle "not found" in else.