PythonMastery
reference 6 min read · lesson 22 of 25 in Reference

Python Loops Cheat Sheet

1 · The lesson

read

A 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.

python
prices = [4.5, 12.0, 3.25]
names = ["Ada", "Grace", "Linus"]

for p in prices:
    print(p)
python
4.5
12.0
3.25
python
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.

python
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.

python
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
python
1 Ada
2 Grace
3 Linus
python
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
python
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.

python
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":

python
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.

python
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.

python
for n in [3, 5, 7]:
    if n % 2 == 0:
        print("found an even number:", n)
        break
else:
    print("no even number")
python
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.

python
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.

python
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.

python
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
python
['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.

python
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.

python
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).

python
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.

python
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:

python
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.