Python Loops Cheat Sheet
1 · The lesson
readA loop runs the same lines again: once for each item (for), or as long as a condition is true (while). This page is a short reference with examples you can run. For the full tutorial, see Loops.
1. for: one item at a time
The loop variable takes each value in turn. A list gives its items, a string gives one character at a time, and a dict gives its keys.
prices = [4.5, 12.0, 3.25] names = ["Ada", "Grace", "Linus"] for p in prices: print(p)
4.5 12.0 3.25
letters = [] for c in "Ada": letters.append(c) print(letters) # ['A', 'd', 'a'] keys = [] for k in {"tea": 3, "pie": 4}: keys.append(k) print(keys) # ['tea', 'pie']
2. range: a run of numbers
range starts at 0 by default and stops before the stop number. A third number is the step.
print(list(range(5))) # [0, 1, 2, 3, 4] print(list(range(2, 6))) # [2, 3, 4, 5] print(list(range(0, 10, 3))) # [0, 3, 6, 9] print(list(range(5, 0, -1))) # [5, 4, 3, 2, 1] for i in range(3): # repeat something 3 times print("Hello")
3. enumerate and zip
enumerate gives a count with each item. zip pairs up items from two lists, and stops at the end of the shorter one.
for i, name in enumerate(names, start=1): print(i, name)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) names = _AutoMock('names') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'prices = [4.5, 12.0, 3.25]\nnames = ["Ada", "Grace", "Linus"]\n\nfor p in prices:\n print(p)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
1 Ada 2 Grace 3 Linus
scores = [90, 85, 77] for name, score in zip(names, scores): print(name, score)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) names = _AutoMock('names') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'prices = [4.5, 12.0, 3.25]\nnames = ["Ada", "Grace", "Linus"]\n\nfor p in prices:\n print(p)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Ada 90 Grace 85 Linus 77
4. while: repeat while a condition is true
The condition is checked before each pass, that is, before each run of the loop body. Use while when you do not know how many passes you need.
money, years = 1000, 0 while money < 2000: money = money * 1.1 # 10% growth each year years += 1 print(years) # 8
while True: never stops on its own, so the loop needs a break. This is the usual way to write "keep asking until the user types q":
answers = ["4.5", "12", "q", "99"] # these answers take the place of input() for_sale = [] while True: answer = answers.pop(0) if answer == "q": break for_sale.append(float(answer)) print(for_sale) # [4.5, 12.0]
5. break and continue
break stops the loop at once. continue skips the rest of the current pass and goes on to the next item.
temps = [18, -3, 35, 19] hot = None for t in temps: if t > 30: hot = t break print(hot) # 35 ok = [] for t in temps: if t < 0: continue # skip the bad reading ok.append(t) print(ok) # [18, 35, 19]
6. else on a loop
The else block runs when the loop ends without break. Read it as "if no break". It works the same on a while loop.
for n in [3, 5, 7]: if n % 2 == 0: print("found an even number:", n) break else: print("no even number")
no even number
With [3, 4, 7], the loop prints found an even number: 4, breaks, and the else block is skipped.
7. Nested loops
The inner loop runs fully for each pass of the outer loop. A break in the inner loop leaves only the inner loop.
labels = [] for size in "SM": for colour in ["red", "blue"]: labels.append(size + "-" + colour) print(labels) # ['S-red', 'S-blue', 'M-red', 'M-blue']
8. Loop over a dict
.items() gives each key and value together.
stock = {"tea": 3, "pie": 0, "cake": 5}
empty = []
for item, qty in stock.items():
if qty == 0:
empty.append(item)
print(empty) # ['pie']9. Order: reversed and sorted
reversed and sorted both give the items in a new order. The original list is not changed.
print(list(reversed(names))) for p in sorted(prices): print(p) print(prices)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) prices = _AutoMock('prices') names = _AutoMock('names') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'prices = [4.5, 12.0, 3.25]\nnames = ["Ada", "Grace", "Linus"]\n\nfor p in prices:\n print(p)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
['Linus', 'Grace', 'Ada'] 3.25 4.5 12.0 [4.5, 12.0, 3.25]
10. Build a result
The pattern is always the same: start with an empty result, then update it once for each item. For the common cases, a built-in function does the same work.
total = 0 for p in prices: total += p print(total, sum(prices)) # 19.75 19.75 big = prices[0] for p in prices: if p > big: big = p print(big, max(prices)) # 12.0 12.0 count = 0 for p in prices: if p > 4: count += 1 print(count) # 2
setup added so this can run · re-runs 1 earlier block on this page first
prices = [3, -1, 4, -1, 5] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'prices = [4.5, 12.0, 3.25]\nnames = ["Ada", "Grace", "Linus"]\n\nfor p in prices:\n print(p)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
11. The short form: a comprehension
A loop that only builds a list can be written in one line. See the comprehensions cheat sheet for more.
print([p * 2 for p in prices]) # [9.0, 24.0, 6.5] print([p for p in prices if p < 5]) # [4.5, 3.25]
setup added so this can run · re-runs 1 earlier block on this page first
prices = [3, -1, 4, -1, 5] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'prices = [4.5, 12.0, 3.25]\nnames = ["Ada", "Grace", "Linus"]\n\nfor p in prices:\n print(p)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Common mistakes
Off by one with range. The stop number is not included. For 1 to 5, write range(1, 6).
print(list(range(1, 5))) # [1, 2, 3, 4] print(list(range(1, 6))) # [1, 2, 3, 4, 5]
Changing a list while you loop over it. Removing an item moves the later items one place to the left, so the loop skips some of them. Loop over a copy, nums[:], or build a new list.
nums = [1, 2, 2, 3] for n in nums: if n == 2: nums.remove(n) print(nums) # [1, 2, 3] nums = [1, 2, 2, 3] for n in nums[:]: # the fix: loop over a copy if n == 2: nums.remove(n) print(nums) # [1, 3]
A while loop that never ends. In n = 0 then while n < 5: print(n), nothing changes n, so the condition stays true for ever. Add n += 1 inside the loop.
range(len(...)) when enumerate is clearer. for i in range(len(names)): print(i, names[i]) works, but for i, name in enumerate(names): is easier to read.
The loop variable after the loop. The loop variable still exists after the loop, and it holds the last item:
for name in names: pass print(name) # Linus
setup added so this can run · re-runs 1 earlier block on this page first
names = ["alpha", "beta", "gamma"] # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'prices = [4.5, 12.0, 3.25]\nnames = ["Ada", "Grace", "Linus"]\n\nfor p in prices:\n print(p)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Found this useful? Share it with a friend who’s learning Python.