9.5. Iterator Zip
Combine two or more sequences
Lazy evaluated
zip(*iterables, strict=False)
required
*iterables
- 1 or many sequences or iterator objectIterate over several iterables in parallel, producing tuples with an item from each one.
The zip
object yields n-length tuples, where n is the number of iterables
passed as positional arguments to zip()
. The i-th element in every tuple
comes from the i-th iterable argument to zip()
. This continues until the
shortest argument is exhausted. If strict is true and one of the arguments is
exhausted before the others, raise a ValueError
. [2]
9.5.1. Problem
Using while
loop:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = []
>>> length = min(len(data1), len(data2))
>>> i = 0
>>> while i < length:
... a = data1[i]
... b = data2[i]
... result.append((a,b))
... i += 1
Using for
loop:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = []
>>> count = min(len(data1), len(data2))
>>> for i in range(count):
... a = data1[i]
... b = data2[i]
... result.append((a,b))
9.5.2. Solution
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(data1, data2)
9.5.3. Lazy Evaluation
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(data1, data2)
>>>
>>> next(result)
('Mark', 'Watney')
>>>
>>> next(result)
('Melissa', 'Lewis')
>>>
>>> next(result)
('Rick', 'Martinez')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration
9.5.4. Iteration
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> for result in zip(data1, data2):
... print(result)
('Mark', 'Watney')
('Melissa', 'Lewis')
('Rick', 'Martinez')
9.5.5. Unpacking
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> for firstname, lastname in zip(data1, data2):
... print(f'{firstname=}, {lastname=}')
firstname='Mark', lastname='Watney'
firstname='Melissa', lastname='Lewis'
firstname='Rick', lastname='Martinez'
9.5.6. As List
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(data1, data2)
>>> list(result)
[('Mark', 'Watney'), ('Melissa', 'Lewis'), ('Rick', 'Martinez')]
9.5.7. As Dict
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(data1, data2)
>>> dict(result)
{'Mark': 'Watney', 'Melissa': 'Lewis', 'Rick': 'Martinez'}
9.5.8. Many Iterables
Lazy Evaluation:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>> data3 = ['botanist', 'commander', 'pilot']
>>>
>>> result = zip(data1, data2, data3)
>>>
>>> next(result)
('Mark', 'Watney', 'botanist')
>>>
>>> next(result)
('Melissa', 'Lewis', 'commander')
>>>
>>> next(result)
('Rick', 'Martinez', 'pilot')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration
As list:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>> data3 = ['botanist', 'commander', 'pilot']
>>>
>>> result = zip(data1, data2, data3)
>>> list(result)
[('Mark', 'Watney', 'botanist'),
('Melissa', 'Lewis', 'commander'),
('Rick', 'Martinez', 'pilot')]
As dict:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis', 'Martinez']
>>> data3 = ['botanist', 'commander', 'pilot']
>>>
>>> result = zip(data1, data2, data3)
>>> dict(result)
Traceback (most recent call last):
ValueError: dictionary update sequence element #0 has length 3; 2 is required
9.5.9. Zip Shortest
zip()
adjusts to the shortest
zip()
is often used in cases where the iterables are assumed to be
of equal length. If the length differs it will silently exit iterator.
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis']
>>>
>>> result = zip(data1, data2)
>>> list(result)
[('Mark', 'Watney'), ('Melissa', 'Lewis')]
9.5.10. Strict
zip(*iterables, strict=False)
Since Python 3.10: PEP 618 -- Add Optional Length-Checking To zip [1]
Source [2]
If the lengths of the iterables can differ, it's recommended to use the
strict=True
option. Without the strict=True
argument, any bug
that results in iterables of different lengths will be silenced, possibly
manifesting as a hard-to-find issue in another part of the program. If the
lengths are equal, the output is the same as regular zip()
. However,
zip()
with strict=True
checks that the lengths of the iterables
are identical, raising a ValueError
if they aren't.
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis']
>>>
>>> result = zip(data1, data2, strict=True)
>>> list(result)
Traceback (most recent call last):
ValueError: zip() argument 2 is shorter than argument 1
9.5.11. Zip Longest
from itertools import zip_longest
zip_longest(*iterables, [fillvalue=None])
SetUp:
>>> from itertools import zip_longest
Usage:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis']
>>>
>>> result = zip_longest(data1, data2)
>>> list(result)
[('Mark', 'Watney'), ('Melissa', 'Lewis'), ('Rick', None)]
Fill Value:
>>> data1 = ['Mark', 'Melissa', 'Rick']
>>> data2 = ['Watney', 'Lewis']
>>>
>>> result = zip_longest(data1, data2, fillvalue='n/a')
>>> list(result)
[('Mark', 'Watney'), ('Melissa', 'Lewis'), ('Rick', 'n/a')]
9.5.12. Use Case - 1
>>> for user, address, order in zip(users, addresses, orders):
... print(f'Get {user} orders... {order}')
9.5.13. References
9.5.14. Assignments
# %% 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
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Iterator Zip Dict
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% English
# 1. Define `result: dict`
# 2. Assign to `result` zipped `KEYS` and `VALUES` to `dict`
# 3. Use `zip()`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result: dict`
# 2. Przypisz do `result` zzipowane `KEYS` i `VALUES` do `dict`
# 3. Użyj `zip()`
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `dict()`
# - `zip()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert type(result) is dict, \
'Variable `result` has invalid type, should be dict'
>>> assert all(type(x) is str for x in result.keys()), \
'All dict keys should be str'
>>> assert 'sepal_length' in result.keys()
>>> assert 'sepal_width' in result.keys()
>>> assert 'petal_length' in result.keys()
>>> assert 'petal_width' in result.keys()
>>> assert 'species' in result.keys()
>>> assert 5.8 in result.values()
>>> assert 2.7 in result.values()
>>> assert 5.1 in result.values()
>>> assert 1.9 in result.values()
>>> assert 'virginica' in result.values()
>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
{'sepal_length': 5.8,
'sepal_width': 2.7,
'petal_length': 5.1,
'petal_width': 1.9,
'species': 'virginica'}
"""
KEYS = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']
VALUES = [5.8, 2.7, 5.1, 1.9, 'virginica']
# Dict with Zipped KEYS and VALUES
# type: dict[str,float|str]
result = ...
# %% 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
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Iterator Zip List[Dict]
# - Difficulty: easy
# - Lines: 2
# - Minutes: 5
# %% English
# 1. Define `result: list[dict]`:
# 2. Convert `DATA` from `list[tuple]` to `list[dict]`
# - key - name from the header
# - value - numerical value or species name
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result: list[dict]`:
# 2. Przekonwertuj `DATA` z `list[tuple]` do `list[dict]`
# - klucz - nazwa z nagłówka
# - wartość - wartość numeryczna lub nazwa gatunku
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - list comprehension
# - `dict()`
# - `zip()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> result = list(result)
>>> assert type(result) is list, \
'Result must be a list'
>>> assert len(result) > 0, \
'Result cannot be empty'
>>> assert all(type(element) is dict for element in result), \
'All elements in result must be a dict'
>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[{'sepal_length': 5.8,
'sepal_width': 2.7,
'petal_length': 5.1,
'petal_width': 1.9,
'species': 'virginica'},
{'sepal_length': 5.1,
'sepal_width': 3.5,
'petal_length': 1.4,
'petal_width': 0.2,
'species': 'setosa'},
{'sepal_length': 5.7,
'sepal_width': 2.8,
'petal_length': 4.1,
'petal_width': 1.3,
'species': 'versicolor'},
{'sepal_length': 6.3,
'sepal_width': 2.9,
'petal_length': 5.6,
'petal_width': 1.8,
'species': 'virginica'},
{'sepal_length': 6.4,
'sepal_width': 3.2,
'petal_length': 4.5,
'petal_width': 1.5,
'species': 'versicolor'},
{'sepal_length': 4.7,
'sepal_width': 3.2,
'petal_length': 1.3,
'petal_width': 0.2,
'species': 'setosa'}]
"""
DATA = [
('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa'),
]
# Convert DATA from list[tuple] to list[dict]
# type: list[dict[str,float|str]]
result = ...
# %% 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
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Iterator Zip Dict
# - Difficulty: easy
# - Lines: 1
# - Minutes: 3
# %% English
# 1. Define `result: zip` with enumerated `DATA`
# 2. Recreate `enumerate()` behavior
# 3. Use only: `len()`, `range()`, `zip()`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result: zip` z enumerowanym `DATA`
# 2. Odtwórz zachowanie `enumerate()`
# 3. Użyj tylko: `len()`, `range()`, `zip()`
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `zip()`
# - `range()`
# - `len()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert type(result) is zip
>>> next(result)
(0, 'January')
>>> next(result)
(1, 'February')
>>> next(result)
(2, 'March')
>>> next(result)
(3, 'April')
>>> next(result)
Traceback (most recent call last):
StopIteration
"""
DATA = ['January', 'February', 'March', 'April']
# Define `result: zip` with enumerated `DATA
# Recreate `enumerate()` behavior
# Use only: `len()`, `range()`, `zip()`
# type: zip
result = ...
# %% 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
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Iterator Zip Impl
# - Difficulty: medium
# - Lines: 11
# - Minutes: 13
# %% English
# 1. Write own implementation of a built-in `zip()` function
# 2. Define function `myzip` with parameters:
# - parameter `a: list | tuple`
# - parameter `b: list | tuple`
# - parameter `strict: bool`
# 3. Don't validate arguments and assume, that user will:
# - always pass valid type of arguments
# - iterable length will always be greater than 0
# - user can only pass two iterables: `a`, `b`
# 4. Do not use built-in function `zip()`
# 5. Run doctests - all must succeed
# %% Polish
# 1. Zaimplementuj własne rozwiązanie wbudowanej funkcji `zip()`
# 2. Zdefiniuj funkcję `myzip` z parametrami:
# - parametr `a: list | tuple`
# - parametr `b: list | tuple`
# - parametr `strict: bool`
# 3. Nie waliduj argumentów i przyjmij, że użytkownik:
# - zawsze poda argumenty poprawnych typów
# - długość iterable będzie większa od 0
# - użytkownik może podać tylko dwie iterable: `a`, `b`
# 4. Nie używaj wbudowanej funkcji `zip()`
# 5. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `min()`
# - `len()`
# - `range()`
# - `list.append()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction
>>> assert isfunction(myzip)
>>> list(myzip(['a', 'b', 'c'], [1, 2, 3]))
[('a', 1), ('b', 2), ('c', 3)]
>>> dict(myzip(['a', 'b', 'c'], [1, 2, 3]))
{'a': 1, 'b': 2, 'c': 3}
>>> dict(myzip(['a', 'b', 'c'], [1, 2, 3, 4]))
{'a': 1, 'b': 2, 'c': 3}
>>> dict(myzip(['a', 'b', 'c'], [1, 2, 3], strict=True))
{'a': 1, 'b': 2, 'c': 3}
>>> dict(myzip(['a', 'b', 'c'], [1, 2, 3, 4], strict=True))
Traceback (most recent call last):
ValueError: zip() argument 2 is longer than argument 1
>>> dict(zip(['a', 'b', 'c', 'd'], [1, 2, 3], strict=True))
Traceback (most recent call last):
ValueError: zip() argument 2 is shorter than argument 1
"""
# Write own implementation of a built-in `zip()` function
# Define function `myrange` with parameters: `a`, `b`, `strict`
# type: Callable[[Iterable, Iterable, bool], list[tuple]]
def myzip(a, b, strict=False):
...