10.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]
10.5.1. Problemï
Using while loop:
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> 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 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> result = []
>>> count = min(len(data1), len(data2))
>>> for i in range(count):
... a = data1[i]
... b = data2[i]
... result.append((a,b))
10.5.2. Solutionï
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> result = zip(data1, data2)
10.5.3. Lazy Evaluationï
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> result = zip(data1, data2)
>>>
>>> next(result)
('Alice', 'Apricot')
>>>
>>> next(result)
('Bob', 'Blackthorn')
>>>
>>> next(result)
('Carol', 'Corn')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration
10.5.4. Iterationï
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> for result in zip(data1, data2):
... print(result)
('Alice', 'Apricot')
('Bob', 'Blackthorn')
('Carol', 'Corn')
10.5.5. Unpackingï
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> for firstname, lastname in zip(data1, data2):
... print(f'{firstname=}, {lastname=}')
firstname='Alice', lastname='Apricot'
firstname='Bob', lastname='Blackthorn'
firstname='Carol', lastname='Corn'
10.5.6. As Listï
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> result = zip(data1, data2)
>>> list(result)
[('Alice', 'Apricot'), ('Bob', 'Blackthorn'), ('Carol', 'Corn')]
10.5.7. As Dictï
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>>
>>> result = zip(data1, data2)
>>> dict(result)
{'Alice': 'Apricot', 'Bob': 'Blackthorn', 'Carol': 'Corn'}
10.5.8. Many Iterablesï
Lazy Evaluation:
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>> data3 = ['alice@example.com', 'bob@example.com', 'carol@example.com']
>>>
>>> result = zip(data1, data2, data3)
>>>
>>> next(result)
('Alice', 'Apricot', 'alice@example.com')
>>>
>>> next(result)
('Bob', 'Blackthorn', 'bob@example.com')
>>>
>>> next(result)
('Carol', 'Corn', 'carol@example.com')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration
As list:
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>> data3 = ['alice@example.com', 'bob@example.com', 'carol@example.com']
>>>
>>> result = zip(data1, data2, data3)
>>> list(result)
[('Alice', 'Apricot', 'alice@example.com'),
('Bob', 'Blackthorn', 'bob@example.com'),
('Carol', 'Corn', 'carol@example.com')]
As dict:
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn', 'Corn']
>>> data3 = ['alice@example.com', 'bob@example.com', 'carol@example.com']
>>>
>>> result = zip(data1, data2, data3)
>>> dict(result)
Traceback (most recent call last):
ValueError: dictionary update sequence element #0 has length 3; 2 is required
10.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 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn']
>>>
>>> result = zip(data1, data2)
>>> list(result)
[('Alice', 'Apricot'), ('Bob', 'Blackthorn')]
10.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 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn']
>>>
>>> result = zip(data1, data2, strict=True)
>>> list(result)
Traceback (most recent call last):
ValueError: zip() argument 2 is shorter than argument 1
10.5.11. Zip Longestï
from itertools import zip_longestzip_longest(*iterables, [fillvalue=None])
SetUp:
>>> from itertools import zip_longest
Usage:
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn']
>>>
>>> result = zip_longest(data1, data2)
>>> list(result)
[('Alice', 'Apricot'), ('Bob', 'Blackthorn'), ('Carol', None)]
Fill Value:
>>> data1 = ['Alice', 'Bob', 'Carol']
>>> data2 = ['Apricot', 'Blackthorn']
>>>
>>> result = zip_longest(data1, data2, fillvalue='n/a')
>>> list(result)
[('Alice', 'Apricot'), ('Bob', 'Blackthorn'), ('Carol', 'n/a')]
10.5.12. Unzipï
Reverse operation of
zip()Using the unpacking operator
*data = [('Alice', 'Apricot'), ('Bob', 'Blackthorn'), ('Carol', 'Corn')]firstnames, lastnames = zip(*data)
>>> data = [('Alice', 'Apricot'), ('Bob', 'Blackthorn'), ('Carol', 'Corn')]
>>>
>>> firstnames, lastnames = zip(*data)
>>>
>>> firstnames
('Alice', 'Bob', 'Carol')
>>>
>>> lastnames
('Apricot', 'Blackthorn', 'Corn')
10.5.13. Use Case - 1ï
>>> for user, address, order in zip(users, addresses, orders):
... print(f'Get {user} orders... {order}')
10.5.14. Referencesï
10.5.15. Assignmentsï
# %% About
# - Name: Iterator Zip Dict
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% 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. Zip `HEADER` and `ROW` to `dict`
# 2. Define `result: dict` with the result
# 3. Use `zip()`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Zzipuj `HEADER` i `ROW` do `dict`
# 2. Zdefiniuj `result: dict` z wynikiem
# 3. Użyj `zip()`
# 4. Uruchom doctesty - wszystkie muszÄ
siÄ powieÅÄ
# %% Expected
# >>> result
# {'firstname': 'Alice', 'lastname': 'Apricot', 'age': 30}
# %% Hints
# - `dict()`
# - `zip()`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert type(result) is dict, \
'Variable `result` has an invalid type; expected: `dict`.'
>>> assert all(type(x) is str for x in result.keys()), \
'Variable `result.keys()` has elements of an invalid type; all items should be: `str`.'
>>> result
{'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 -f -v myfile.py`
# %% Imports
# %% Types
result: dict[str,str|int]
# %% Data
HEADER = ('firstname', 'lastname', 'age')
ROW = ('Alice', 'Apricot', 30)
# %% Result
result = ...
# %% About
# - Name: Iterator Zip ToListDict
# - Difficulty: easy
# - Lines: 2
# - 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
# 1. Convert `DATA` from `list[tuple]` to `list[dict]`
# 2. First row has column names (keys in result `dict`)
# 2. Define variable `result` with the result
# 3. Run doctests - all must succeed
# %% Polish
# 1. Przekonwertuj `DATA` z `list[tuple]` do `list[dict]`
# 2. Pierwszy wiersz ma nazwy kolumn (klucze w wynikowym `dict`)
# 3. Zdefiniuj zmiennÄ
`result` z wynikiem
# 4. Uruchom doctesty - wszystkie muszÄ
siÄ powieÅÄ
# %% Expected
# >>> result
# [{'firstname': 'Alice', 'lastname': 'Apricot', 'age': 30},
# {'firstname': 'Bob', 'lastname': 'Blackthorn', '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}]
# %% Why
# - Convert data from `list[tuple]` to `list[dict]`
# - `list[tuple]` is used to represent CSV data
# - `list[tuple]` is used to represent database rows
# - `list[dict]` is used to represent JSON data
# - CSV is the most popular format in data science
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert 'result' in globals(), \
'Variable `result` is not defined; assign result of your program to it.'
>>> assert result is not Ellipsis, \
'Variable `result` has an invalid value; assign result of your program to it.'
>>> result = list(result)
>>> assert type(result) is list, \
'Variable `result` has an invalid type; expected: `list`.'
>>> assert len(result) > 0, \
'Variable `result` has an invalid length; expected more than zero elements.'
>>> assert all(type(x) is dict for x in result), \
'Variable `result` has elements of an invalid type; all items should be: `dict`.'
>>> from pprint import pprint
>>> pprint(result, sort_dicts=False)
[{'firstname': 'Alice', 'lastname': 'Apricot', 'age': 30},
{'firstname': 'Bob', 'lastname': 'Blackthorn', '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 -f -v myfile.py`
# %% Imports
# %% Types
result: list[dict[str,str|int]]
# %% Data
DATA = [
('firstname', 'lastname', 'age'),
('Alice', 'Apricot', 30),
('Bob', 'Blackthorn', 31),
('Carol', 'Corn', 32),
('Dave', 'Durian', 33),
('Eve', 'Elderberry', 34),
('Mallory', 'Melon', 15),
]
# %% Result
result = ...
# %% About
# - Name: Iterator Zip Impl
# - Difficulty: medium
# - Lines: 11
# - Minutes: 13
# %% 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. Write own implementation of a built-in `zip()` function
# 2. Define function `myzip` with parameters:
# - `a: list|tuple` - first iterable
# - `b: list|tuple` - second iterable
# - `strict: bool` - flag, default False
# 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. If `strict` is True and `b` is longer than `b` then raise
# `ValueError` with message: 'myzip() argument 2 is longer than argument 1'
# 5. If `strict` is True and `b` is shorter than `a` then raise
# `ValueError` with message: 'myzip() argument 2 is shorter than argument 1'
# 6. Do not use built-in function `zip()`
# 7. Use `yield` keyword
# 8. Run doctests - all must succeed
# %% Polish
# 1. Zaimplementuj wÅasne rozwiÄ
zanie wbudowanej funkcji `zip()`
# 2. Zdefiniuj funkcjÄ `myzip` z parametrami:
# - `a: list|tuple` - pierwsza iterable
# - `b: list|tuple` - drugie iterable
# - `strict: bool` - flaga, domyÅlnie False
# 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. Jeżeli `strict` jest `True` oraz `b` jest dÅuższe niż `a` to podnieÅ
# `ValueError` z komunikatem: 'myzip() argument 2 is longer than argument 1'
# 5. Jeżeli `strict` jest `True` oraz `b` jest krótsze niż `a` to podnieÅ
# `ValueError` z komunikatem: 'myzip() argument 2 is shorter than argument 1'
# 6. Nie używaj wbudowanej funkcji `zip()`
# 7. Użyj sÅowa kluczowego `yield`
# 8. Uruchom doctesty - wszystkie muszÄ
siÄ powieÅÄ
# %% Expected
# >>> 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]))
# {'a': 1, 'b': 2, 'c': 3}
#
# >>> dict(myzip(['a', 'b', 'c', 'd'], [1, 2, 3]))
# {'a': 1, 'b': 2, 'c': 3}
#
# >>> dict(myzip(['a', 'b', 'c'], [1, 2, 3, 4], strict=True))
# Traceback (most recent call last):
# ValueError: myzip() argument 2 is longer than argument 1
#
# >>> dict(myzip(['a', 'b', 'c', 'd'], [1, 2, 3], strict=True))
# Traceback (most recent call last):
# ValueError: myzip() argument 2 is shorter than argument 1
# %% Hints
# - `min()`
# - `len()`
# - `range()`
# - `list.append()`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> 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]))
{'a': 1, 'b': 2, 'c': 3}
>>> dict(myzip(['a', 'b', 'c', 'd'], [1, 2, 3]))
{'a': 1, 'b': 2, 'c': 3}
>>> dict(myzip(['a', 'b', 'c'], [1, 2, 3, 4], strict=True))
Traceback (most recent call last):
ValueError: myzip() argument 2 is longer than argument 1
>>> dict(myzip(['a', 'b', 'c', 'd'], [1, 2, 3], strict=True))
Traceback (most recent call last):
ValueError: myzip() argument 2 is shorter than argument 1
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
# %% Types
from typing import Callable
myzip: Callable[[tuple|list, tuple|list, bool], list[tuple]]
# %% Data
# %% Result
def myzip(a, b, strict=False):
...