for

Python’s only counting-free loop: it walks an iterable item by item — and its optional else runs only when no break happened.

Control flowPython 3 (all)Live demo
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

NameTypeRequiredDescription
targetname / patternyesAssigned each item in turn. Can unpack: for i, x in … . Still bound to the last item after the loop.
iterableexpressionyesEvaluated once, before the first iteration. Anything with __iter__ (or __getitem__).
bodyblockyesRuns once per item. break leaves the loop, continue skips to the next item.
elseblocknoRuns 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 pear
2. range() counts
for i in range(3): print(i)
Returns
0 1 2
3. 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 b
5. Unpacking in the target
for name, age in [('Ann', 31), ('Bo', 25)]: print(name, age)
Returns
Ann 31 Bo 25
6. The target outlives the loop
for i in range(3): pass i
Returns
2
7. else runs when nothing broke
for x in [1, 3, 5]: if x % 2 == 0: break else: print('no even number')
Returns
no even number

Pitfalls

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.