12.4. For Patterns

  • Range - when you need a sequential number

  • Range of Length - when you need: index

  • Enumerate - when you need: index and element

  • Zip - when you need elements at the same index from both A and B sequences

12.4.1. Range

  • range(stop)

  • range(start, stop)

  • range(start, stop, step)

  • Negative step means reverse order

Range from 0 to 5:

>>> for x in range(0, 5):
...     print(x)
...
0
1
2
3
4

5-times (equivalent to range(0,5)):

>>> for x in range(5):
...     print(x)
...
0
1
2
3
4

Step (each second element from 0 to 5):

>>> for x in range(0, 5, 2):
...     print(x)
...
0
2
4

Negative step (each element from 5 to 0):

>>> for x in range(5, 0, -1):
...     print(x)
...
5
4
3
2
1

Negative step (each second element from 5 to 0):

>>> for x in range(5, 0, -2):
...     print(x)
...
5
3
1

12.4.2. Range of Length

  • In most cases it's a bad practice

  • This is a pattern taken from other languages and applied to Python

  • In Python we use for loop for doing that

  • That's because Python for loop is a foreach loop

  • Also for x in range(len(...)) twice as slow as plain for x in ...

>>> users = ['alice', 'bob', 'carol']
>>>
>>> for i in range(len(users)):
...     user = users[i]
...     print(user)
...
alice
bob
carol

But the following is more Pythonic:

>>> users = ['alice', 'bob', 'carol']
>>>
>>> for user in users:
...     print(user)
...
alice
bob
carol

The output is exactly the same. The second example is more Pythonic and easier to read.

Performance:

>>> users = ['alice', 'bob', 'carol']
>>>
>>> # doctest: +SKIP
... %%timeit -r1000 -n1000
... for user in users:
...     pass
...
36.1 ns ± 7.42 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
34.1 ns ± 5.85 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
36.6 ns ± 8.1 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
36.6 ns ± 7 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
34.6 ns ± 8.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> users = ['alice', 'bob', 'carol']
>>>
>>> # doctest: +SKIP
... %%timeit -r1000 -n 1000
... for i in range(len(users)):
...     user = users[i]
...
72.5 ns ± 15.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
71.3 ns ± 14.6 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
71 ns ± 14.8 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
71.8 ns ± 14.8 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
72.9 ns ± 16.5 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
  • Date: 2024-12-05

  • Python: 3.13.0

  • IPython: 8.30.0

  • System: macOS 15.1.1

  • Computer: MacBook M3 Max

  • CPU: 16 cores (12 performance and 4 efficiency) / 3nm

  • RAM: 128 GB RAM LPDDR5

12.4.3. Enumerate

  • Later you will learn builtin enumerate() function which does that

>>> users = ['alice', 'bob', 'carol']
>>> i = 0
>>>
>>> for user in users:
...     print(f'{i=}, {user=}')
...     i += 1
...
i=0, user='alice'
i=1, user='bob'
i=2, user='carol'

12.4.4. Zip

  • Later you will learn builtin zip() function which does that

>>> dataA = ['Alice', 'Bob', 'Carol']
>>> dataB = ['Apricot', 'Banana', 'Corn']
>>>
>>> times = min(len(dataA), len(dataB))  # 3
>>>
>>> for i in range(times):  # for i in range(3)
...     a = dataA[i]
...     b = dataB[i]
...     print(f'{a=}, {b=}')
...
a='Alice', b='Apricot'
a='Bob', b='Banana'
a='Carol', b='Corn'

Because:

>>> dataA = ['Alice', 'Bob', 'Carol']
>>> dataB = ['Apricot', 'Banana', 'Corn']
>>>
>>>
>>> a = dataA[0]
>>> b = dataB[0]
>>> print(f'{a=}, {b=}')
a='Alice', b='Apricot'
>>>
>>> a = dataA[1]
>>> b = dataB[1]
>>> print(f'{a=}, {b=}')
a='Bob', b='Banana'
>>>
>>> a = dataA[2]
>>> b = dataB[2]
>>> print(f'{a=}, {b=}')
a='Carol', b='Corn'

12.4.5. Recap

  • Range - when you need a sequential number

  • Range of Length - when you need: index

  • Enumerate - when you need: index and element

  • Zip - when you need elements at the same index from both A and B sequences

Range:

>>> for x in range(3):
...     print(x)
...
0
1
2

Range of Length:

>>> users = ['alice', 'bob', 'carol']
>>>
>>> for i in range(len(users)):
...     user = users[i]
...     print(user)
...
alice
bob
carol

Enumerate:

>>> users = ['alice', 'bob', 'carol']
>>> i = 0
>>>
>>> for user in users:
...     print(f'{i=}, {user=}')
...     i += 1
...
i=0, user='alice'
i=1, user='bob'
i=2, user='carol'

Zip:

>>> dataA = ['Alice', 'Bob', 'Carol']
>>> dataB = ['Apricot', 'Banana', 'Corn']
>>>
>>> times = min(len(dataA), len(dataB))  # 3
>>>
>>> for i in range(times):  # for i in range(3)
...     a = dataA[i]
...     b = dataB[i]
...     print(f'{a=}, {b=}')
...
a='Alice', b='Apricot'
a='Bob', b='Banana'
a='Carol', b='Corn'

12.4.6. Assignments

# %% About
# - Name: For Patterns Range
# - Difficulty: easy
# - Lines: 3
# - Minutes: 3

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Define `result: list[int]` with numbers from 0 (inclusive) to 5 (exclusive)
# 2. Use for-range pattern
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj `result: list[int]` z liczbami od 0 (włącznie) do 5 (rozłącznie)
# 2. Użyj wzorca for-range
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# [0, 1, 2, 3, 4]

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'
>>> assert all(type(x) is int for x in result), \
'All elements in `result` should be int'

>>> from pprint import pprint
>>> pprint(result, width=79, sort_dicts=False)
[0, 1, 2, 3, 4]
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports

# %% Types
result: list[int]

# %% Data

# %% Result
result = ...

# %% About
# - Name: For Patterns Enumerate
# - Difficulty: easy
# - Lines: 6
# - Minutes: 5

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 0. Recreate `enumerate` behavior without using `enumerate()`
# 1. Define `result: list[tuple]`:
#    - first element: month number (from 1 to 12)
#    - second element: month name (January, February, March, ...)
# 2. Month numbers starts with 1
# 3. Do not use `enumerate()` builtin function
# 4. Run doctests - all must succeed

# %% Polish
# 0. Odtwórz zachowanie `enumerate` bez użycia `enumerate()`
# 1. Zdefiniuj `result: list[tuple]`:
#    - pierwszy element: numer miesiąca (od 1 do 12)
#    - drugi element: nazwa miesiąca (January, February, March, ...)
# 2. Numery miesięcy zaczynają się od 1
# 3. Nie używaj wbudowanej funkcji `enumerate()`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# >>> result
# [(1, 'January'),
#  (2, 'February'),
#  (3, 'March'),
#  ...,
#  (11, 'November'),
#  (12, 'December')]

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'

>>> assert all(type(x) is tuple for x in result), \
'All elements in `result` must be a tuple'

>>> assert len(result) == 12, \
'Variable `result` must contains exactly 12 elements'

>>> assert all(i>0 for i,month in result), \
'Month number must be greater than 0'

>>> assert all(i<13 for i,month in result), \
'Month number must be less than 13'

>>> from pprint import pprint
>>> pprint(result, width=79, sort_dicts=False)
[(1, 'January'),
 (2, 'February'),
 (3, 'March'),
 (4, 'April'),
 (5, 'May'),
 (6, 'June'),
 (7, 'July'),
 (8, 'August'),
 (9, 'September'),
 (10, 'October'),
 (11, 'November'),
 (12, 'December')]
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports

# %% Types
result: list[tuple]

# %% Data
MONTHS = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
]

# %% Result
result = ...

# %% About
# - Name: For Patterns Zip
# - Difficulty: easy
# - Lines: 5
# - Minutes: 5

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 0. Recreate `zip` behavior without using `zip()`
# 1. Define `result: list[tuple]`:
#    - first element - firstname
#    - second element - lastname
# 2. Do not use `zip()` builtin function
# 3. Run doctests - all must succeed

# %% Polish
# 0. Odtwórz zachowanie `zip` bez użycia `zip()`
# 1. Zdefiniuj `result: list[tuple]`:
#    - pierwszy element - imię
#    - drugi element - nazwisko
# 2. Nie używaj wbudowanej funkcji `zip()`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# >>> result
# [('Alice', 'Apricot'),
#  ('Bob', 'Banana'),
#  ('Carol', 'Corn')]

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'

>>> assert all(type(x) is tuple for x in result), \
'All elements in `result` must be a tuple'

>>> from pprint import pprint
>>> pprint(result, width=30, sort_dicts=False)
[('Alice', 'Apricot'),
 ('Bob', 'Banana'),
 ('Carol', 'Corn')]
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports

# %% Types
result: list[tuple]

# %% Data
FIRSTNAMES = ['Alice', 'Bob', 'Carol']
LASTNAMES = ['Apricot', 'Banana', 'Corn']

# %% Result
result = ...

# %% About
# - Name: For Patterns Zip
# - Difficulty: easy
# - Lines: 6
# - Minutes: 5

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 0. Recreate `zip` behavior without using builtin function `zip()`
# 1. Convert `HEADER` and `ROW` to `dict`
# 2. Define `result: list[dict]`:
#    - key: column name from the header (firstname, lastname lub age)
#    - value: value from the row (ie. Alice, Apricot, 30)
# 3. Do not use `zip()` builtin function
# 4. Run doctests - all must succeed

# %% Polish
# 0. Odtwórz zachowanie `zip` bez użycia wbudowanej funkcji `zip()`
# 1. Przekonwertuj `HEADER` i `ROW` do `dict`
# 2. Zdefiniuj `result: list[dict]`:
#    - klucz: nazwa kolumny z nagłówka (firstname, lastname lub age)
#    - wartość: wartość z wiersza (np. Alice, Apricot, 30)
# 3. Nie używaj wbudowanej funkcji `zip()`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# >>> result
# {'firstname': 'Alice',
#  'lastname': 'Apricot',
#  'age': 30}

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is dict, \
'Variable `result` has invalid type, should be dict'

>>> from pprint import pprint
>>> pprint(result, width=30, sort_dicts=False)
{'firstname': 'Alice',
 'lastname': 'Apricot',
 'age': 30}
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports

# %% Types
result: dict[str, str|int]

# %% Data
HEADER = ('firstname', 'lastname', 'age')
ROW = ('Alice', 'Apricot', 30)

# %% Result
result = ...

# %% About
# - Name: For Patterns Iterable Zip
# - Difficulty: medium
# - Lines: 11
# - Minutes: 8

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 0. Recreate `zip` behavior without using builtin function `zip()`
# 1. Convert `list[tuple]` to `list[dict]`
# 2. Define `result: list[dict]`:
#    - key: column name from the header (firstname, lastname lub age)
#    - value: value from the row (ie. Alice, Apricot, 30)
# 3. Do not use `zip()` builtin function
# 4. Run doctests - all must succeed

# %% Polish
# 0. Odtwórz zachowanie `zip` bez użycia wbudowanej funkcji `zip()`
# 1. Przekonwertuj `list[tuple]` do `list[dict]`
# 2. Zdefiniuj `result: list[dict]`:
#    - klucz: nazwa kolumny z nagłówka (firstname, lastname lub age)
#    - wartość: wartość z wiersza (np. Alice, Apricot, 30)
# 3. Nie używaj wbudowanej funkcji `zip()`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Example
# >>> result
# [{'firstname': 'Alice', 'lastname': 'Apricot', 'age': 30},
#  {'firstname': 'Bob', 'lastname': 'Banana', 'age': 31},
#  {'firstname': 'Carol', 'lastname': 'Corn', 'age': 32},
#  {'firstname': 'Dave', 'lastname': 'Durian', 'age': 33},
#  {'firstname': 'Eve', 'lastname': 'Elderberry', 'age': 34},
#  {'firstname': 'Mallory', 'lastname': 'Melon', 'age': 15}]

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'

>>> assert all(type(x) is dict for x in result)

>>> from pprint import pprint
>>> pprint(result, width=120, sort_dicts=False)
[{'firstname': 'Alice', 'lastname': 'Apricot', 'age': 30},
 {'firstname': 'Bob', 'lastname': 'Banana', 'age': 31},
 {'firstname': 'Carol', 'lastname': 'Corn', 'age': 32},
 {'firstname': 'Dave', 'lastname': 'Durian', 'age': 33},
 {'firstname': 'Eve', 'lastname': 'Elderberry', 'age': 34},
 {'firstname': 'Mallory', 'lastname': 'Melon', 'age': 15}]
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports

# %% Types
result: list[dict[str,str|int]]

# %% Data
DATA = [
    ('firstname', 'lastname', 'age'),
    ('Alice', 'Apricot', 30),
    ('Bob', 'Banana', 31),
    ('Carol', 'Corn', 32),
    ('Dave', 'Durian', 33),
    ('Eve', 'Elderberry', 34),
    ('Mallory', 'Melon', 15),
]

# %% Result
result = ...