11.1. FP Apply Map
Converts elements in sequence
Lazy evaluated
map(callable, *iterables)
required
callable
- Functionrequired
iterables
- 1 or many sequence or iterator objects
The map()
function in Python is a built-in function that applies a given
function to each element of an iterable (such as a list, tuple, or set) and
returns a new iterable with the results. It takes two arguments: a function
and an iterable.
The function is applied to each element in the iterable, and the results are collected into a new iterable. The resulting iterable can be converted to a list, tuple, or set if desired.
Here's an example of using the map()
function to square each number
in a list:
>>> def square(n):
... return n ** 2
>>>
>>> data = (1, 2, 3, 4, 5)
>>> result = map(square, data)
>>> tuple(result)
(1, 4, 9, 16, 25)
In this example, the power
function is applied to each element in the
numbers
list using the map()
function. The resulting iterable contains
the squared values of each element in the original tuple
. The tuple()
function is used to convert the iterable to a tuple
.
11.1.1. Problem
>>> def square(x):
... return x ** 2
>>>
>>> data = (1, 2, 3, 4)
Iteration:
>>> result = []
>>> for x in data:
... result.append(square(x))
>>>
>>> tuple(result)
(1, 4, 9, 16)
11.1.2. Solution
List Comprehension
Map
>>> def square(x):
... return x ** 2
>>>
>>> data = (1, 2, 3, 4)
Comprehension:
>>> result = [square(x) for x in data]
>>> tuple(result)
(1, 4, 9, 16)
Map:
>>> result = map(square, data)
>>> tuple(result)
(1, 4, 9, 16)
11.1.3. Lazy Evaluation
>>> def square(x):
... return x ** 2
>>>
>>> data = (1, 2, 3, 4)
Usage:
>>> result = map(square, data)
>>>
>>> next(result)
1
>>>
>>> next(result)
4
>>>
>>> next(result)
9
>>>
>>> next(result)
16
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration
11.1.4. For Loop
>>> def square(x):
... return x ** 2
>>>
>>> data = (1, 2, 3, 4)
Usage:
>>> for result in map(square, data):
... print(result)
1
4
9
16
11.1.5. Multiple Arguments
>>> def power(num, exp):
... return num ** exp
...
>>> numbers = (1, 2, 3, 4)
>>> exponents = (2, 3, 4, 5)
>>>
>>> result = map(power, numbers, exponents)
>>>
>>> tuple(result)
(1, 8, 81, 1024)
Map behind the scenes:
>>> map(pow, numbers, exponents)
<map object at 0x1052f1d80>
# add(numbers[0], exponents[0]) # add(1, 2)
# add(numbers[1], exponents[1]) # add(3, 4)
# add(numbers[2], exponents[2]) # add(5, 6)
11.1.6. Starmap
>>> from itertools import starmap
>>> def power(num, exp):
... return num ** exp
>>>
>>> data = (
... (1, 2),
... (2, 3),
... (3, 4),
... (4, 5),
... )
>>>
>>> result = starmap(power, data)
>>> tuple(result)
(1, 8, 81, 1024)
Map behind the scenes:
>>> map(pow, data)
<map object at 0x1059f62c0>
# add( data[0] ) # add( (1,2) )
# add( data[1] ) # add( (3,4) )
# add( data[2] ) # add( (5,6) )
Starmap behind the scenes:
>>> starmap(pow, data)
<itertools.starmap object at 0x105b19090>
# add( *data[0] ) # add( *(1,2) ) # add(1, 2)
# add( *data[1] ) # add( *(3,4) ) # add(3, 4)
# add( *data[2] ) # add( *(5,6) ) # add(5, 6)
11.1.7. Not A Generator
from inspect import isgeneratorfunction, isgenerator
isgeneratorfunction(callable)
- Check if the object is a generator functionisgenerator(iterator)
- Check if the object is a generator iteratormap()
is not a generator function
SetUp:
>>> from inspect import isgeneratorfunction, isgenerator
Map function:
>>> isgeneratorfunction(map)
False
Map object:
>>> data = (1, 2, 3, 4)
>>> result = map(float, data)
>>>
>>> isgenerator(result)
False
11.1.8. Performance
Date: 2024-12-01
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
>>> def power(x, exp):
... return x ** exp
...
>>> data = (1, 2, 3, 4)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = list(map(square, data))
...
390 ns ± 60.5 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
381 ns ± 57.8 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
386 ns ± 62.7 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = [square(x) for x in data]
...
328 ns ± 58.1 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
329 ns ± 54.6 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
337 ns ± 54.8 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = list(square(x) for x in data)
...
474 ns ± 65.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
479 ns ± 70.8 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
473 ns ± 51.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = (square(x) for x in data)
...
91.1 ns ± 20.4 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
91.1 ns ± 21.8 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
88.6 ns ± 20 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = map(square, data)
...
56.6 ns ± 14 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
54.5 ns ± 10.1 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
57.1 ns ± 12.7 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
11.1.9. Use Case - 1
>>> data = (1, 2, 3, 4)
>>> result = map(float, data)
>>>
>>> tuple(result)
(1.0, 2.0, 3.0, 4.0)
11.1.10. Use Case - 2
>>> def increment(x):
... return x + 1
>>>
>>>
>>> data = (1, 2, 3, 4)
>>> result = map(increment, data)
>>>
>>> tuple(result)
(2, 3, 4, 5)
11.1.11. Use Case - 3
>>> def transform(x):
... return x.upper()
>>>
>>> data = ('a', 'b', 'c')
>>> result = map(transform, data)
>>>
>>> tuple(result)
('A', 'B', 'C')
11.1.12. Use Case - 4
SetUp:
>>> data = (1, 2, 3, 4)
Output formats:
>>> result = map(float, data)
>>> tuple(result)
(1.0, 2.0, 3.0, 4.0)
>>> result = map(float, data)
>>> set(result)
{1.0, 2.0, 3.0, 4.0}
>>> result = map(float, data)
>>> list(result)
[1.0, 2.0, 3.0, 4.0]
11.1.13. Use Case - 5
>>> def translate(letter):
... return PL.get(letter, letter)
>>>
>>> PL = {'ą': 'a', 'ć': 'c', 'ę': 'e',
... 'ł': 'l', 'ń': 'n', 'ó': 'o',
... 'ś': 's', 'ż': 'z', 'ź': 'z'}
>>>
>>> data = 'zażółć gęślą jaźń'
>>> result = map(translate, data)
>>> ''.join(result)
'zazolc gesla jazn'
11.1.14. Use Case - 6
Standard input:
>>> import sys
>>>
>>>
... print(sum(map(int, sys.stdin)))
$ cat ~/.profile |grep addnum
alias addnum='python -c"import sys; print(sum(map(int, sys.stdin)))"'
11.1.15. Use Case - 7
>>> from dataclasses import dataclass
>>> from pprint import pprint
>>>
>>>
>>> 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'),
... (7.0, 3.2, 4.7, 1.4, 'versicolor'),
... (7.6, 3.0, 6.6, 2.1, 'virginica'),
... (4.6, 3.1, 1.5, 0.2, 'setosa'),
... ]
>>>
>>> header, *rows = DATA
>>>
>>> @dataclass
... class Iris:
... sl: float
... sw: float
... pl: float
... pw: float
... species: str
...
>>>
>>> result = starmap(Iris, rows)
>>> pprint(tuple(result))
(Iris(sl=5.8, sw=2.7, pl=5.1, pw=1.9, species='virginica'),
Iris(sl=5.1, sw=3.5, pl=1.4, pw=0.2, species='setosa'),
Iris(sl=5.7, sw=2.8, pl=4.1, pw=1.3, species='versicolor'),
Iris(sl=6.3, sw=2.9, pl=5.6, pw=1.8, species='virginica'),
Iris(sl=6.4, sw=3.2, pl=4.5, pw=1.5, species='versicolor'),
Iris(sl=4.7, sw=3.2, pl=1.3, pw=0.2, species='setosa'),
Iris(sl=7.0, sw=3.2, pl=4.7, pw=1.4, species='versicolor'),
Iris(sl=7.6, sw=3.0, pl=6.6, pw=2.1, species='virginica'),
Iris(sl=4.6, sw=3.1, pl=1.5, pw=0.2, species='setosa'))
Map behind the scenes:
>>> map(Iris, rows)
<map object at 0x1034f5b10>
# Iris(rows[0])
# Iris(rows[1])
# Iris(rows[2])
# ...
# Iris(rows[9])
Starmap behind the scenes:
>>> starmap(Iris, rows)
<itertools.starmap object at 0x103529d80>
# Iris(*rows[0])
# Iris(*rows[1])
# Iris(*rows[2])
# ...
# Iris(*rows[9])
11.1.16. Use Case - 8
>>> import httpx
>>>
>>> url = 'https://python3.info/_static/iris-dirty.csv'
>>>
>>> data = httpx.get(url).text
>>> header, *rows = data.splitlines()
>>> nrows, nfeatures, *class_labels = header.strip().split(',')
>>> label_encoder = dict(enumerate(class_labels))
Procedural:
>>> result = []
>>> for row in rows:
... *features, species = row.strip().split(',')
... features = map(float, features)
... species = label_encoder[int(species)]
... row = tuple(features) + (species,)
... result.append(row)
Functional:
>>> def decode(row):
... *features, species = row.strip().split(',')
... features = map(float, features)
... species = label_encoder[int(species)]
... return tuple(features) + (species,)
>>>
>>> result = map(decode, rows)
Mixed:
>>> def decode(row):
... *features, species = row.strip().split(',')
... features = map(float, features)
... species = label_encoder[int(species)]
... return tuple(features) + (species,)
>>>
>>> with open('/tmp/myfile.csv') as file:
... header = file.readline()
... for line in map(decode, file):
... print(line)
11.1.17. Use Case - 8
SetUp:
>>> from doctest import testmod as run_tests
Data [1]:
>>> DATA = """150,4,setosa,versicolor,virginica
... 5.1,3.5,1.4,0.2,0
... 7.0,3.2,4.7,1.4,1
... 6.3,3.3,6.0,2.5,2
... 4.9,3.0,1.4,0.2,0
... 6.4,3.2,4.5,1.5,1
... 5.8,2.7,5.1,1.9,2"""
Definition:
>>> def get_labelencoder(header: str) -> dict[int, str]:
... """
... >>> get_labelencoder('150,4,setosa,versicolor,virginica')
... {0: 'setosa', 1: 'versicolor', 2: 'virginica'}
... """
... nrows, nfeatures, *class_labels = header.split(',')
... return dict(enumerate(class_labels))
>>>
>>> run_tests()
TestResults(failed=0, attempted=1)
>>> def get_data(line: str) -> tuple:
... """
... >>> convert('5.1,3.5,1.4,0.2,0')
... (5.1, 3.5, 1.4, 0.2, 'setosa')
... >>> convert('7.0,3.2,4.7,1.4,1')
... (7.0, 3.2, 4.7, 1.4, 'versicolor')
... >>> convert('6.3,3.3,6.0,2.5,2')
... (6.3, 3.3, 6.0, 2.5, 'virginica')
... """
... *values, species = line.split(',')
... values = map(float, values)
... species = label_encoder[int(species)]
... return tuple(values) + (species,)
>>>
>>> run_tests()
TestResults(failed=0, attempted=3)
>>> header, *lines = DATA.splitlines()
>>> label_encoder = get_labelencoder(header)
>>> result = map(get_data, lines)
>>> list(result)
[(5.1, 3.5, 1.4, 0.2, 'setosa'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(6.3, 3.3, 6.0, 2.5, 'virginica'),
(4.9, 3.0, 1.4, 0.2, 'setosa'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(5.8, 2.7, 5.1, 1.9, 'virginica')]
11.1.18. References
11.1.19. 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: Functional Map Cube
# - Difficulty: easy
# - Lines: 1
# - Minutes: 3
# %% English
# 1. Use `map()` to apply function `cube()` to DATA
# 2. Define `result: map` with result
# 3. Run doctests - all must succeed
# %% Polish
# 1. Użyj `map()` aby zaaplikować funkcję `cube()` do DATA
# 2. Zdefiniuj `result: map` z wynikiem
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction
>>> assert isfunction(cube), \
'Object `cube` must be a function'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is map, \
'Variable `result` has invalid type, should be map'
>>> result = tuple(result)
>>> assert type(result) is tuple, \
'Evaluated `result` has invalid type, should be tuple'
>>> assert all(type(x) is int for x in result), \
'All rows in `result` should be int'
>>> result
(1, 8, 27, 64)
"""
DATA = (1, 2, 3, 4)
def cube(x):
return x ** 3
# Use `map()` to apply function `cube()` to DATA
# Define `result: map` with result
# type: map[int]
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: Functional Map FromISOFormat
# - Difficulty: easy
# - Lines: 1
# - Minutes: 3
# %% English
# 1. Define `result: map` with parsed `DATA` dates
# 2. Use `map()` and `datetime.fromisoformat()`
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result: map` ze sparsowanymi datami `DATA`
# 2. Użyj `map()` oraz `datetime.fromisoformat()`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `map()`
# - `datetime.fromisoformat()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from pprint import pprint
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is map, \
'Variable `result` has invalid type, must be a map'
>>> result = list(result)
>>> assert type(result) is list, \
'Variable `result` has invalid type, must be a list'
>>> assert all(type(element) is datetime for element in result), \
'All elements in `result` must be a datetime'
>>> pprint(result, width=30)
[datetime.datetime(1961, 4, 12, 6, 7),
datetime.datetime(1961, 4, 12, 6, 7)]
"""
from datetime import datetime
DATA = [
'1961-04-12 06:07',
'1961-04-12 06:07:00',
]
# Define `result: map` with parsed `DATA` dates
# type: map[datetime]
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: Functional Map DateFormats
# - Difficulty: easy
# - Lines: 1
# - Minutes: 3
# %% English
# 1. Define `result: map` with parsed `DATA` dates
# 2. Use `map()`
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result: map` ze sparsowanymi datami `DATA`
# 2. Użyj `map()`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `for ... in`
# - nested `try ... except`
# - `FORMATS = []`
# - `for fmt in FORMATS`
# - helper function
# - 24-hour clock
# - `map(func, iterable1, iterable1)`
# %% 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`'
>>> assert type(result) is map, \
'Variable `result` has invalid type, must be a map'
>>> result = list(result)
>>> assert type(result) is list, \
'Variable `result` has invalid type, must be a list'
>>> assert all(type(element) is datetime for element in result), \
'All elements in `result` must be a datetime'
>>> result # doctest: +NORMALIZE_WHITESPACE
[datetime.datetime(1957, 10, 4, 19, 28, 34),
datetime.datetime(1961, 4, 12, 6, 7),
datetime.datetime(1969, 7, 21, 2, 56, 15)]
"""
from datetime import datetime
DATA = [
'Oct 4, 1957 19:28:34', # Sputnik launch (first satellite in space)
'April 12, 1961 6:07', # Gagarin launch (first human in space)
'July 21, 1969 2:56:15', # Armstrong first step on the Moon
]
FORMATS = [
'%b %d, %Y %H:%M:%S',
'%B %d, %Y %H:%M',
'%B %d, %Y %H:%M:%S',
]
# DATA elements in datetime format
# type: map[datetime]
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: Functional Map Logs
# - Difficulty: medium
# - Lines: 7
# - Minutes: 8
# %% English
# 1. Iterate over `DATA` with Apollo 11 timeline [1]
# 2. From each line extract date, time, level and message
# 3. Collect data to `result: map`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Iteruj po `DATA` z harmonogramem Apollo 11 [1]
# 2. Dla każdej linii wyciągnij datę, czas, poziom logowania oraz wiadomość
# 3. Zbierz dane do `result: map`
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - Note, that last time has no seconds
# - This is not bug, time without seconds is in NASA history records [1]
# %% References
# [1] National Aeronautics and Space Administration.
# Apollo 11 timeline.
# Year: 1969. Retrieved: 2021-03-25.
# URL: https://history.nasa.gov/SP-4029/Apollo_11i_Timeline.htm
# %% Hints
# - `str.splitlines()`
# - `str.split(', ', maxsplit=3)`
# - `date.fromisoformat()`
# - `time.fromisoformat()`
# - `datetime.combine()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from pprint import pprint
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is map, \
'Variable `result` has invalid type, must be a map'
>>> result = list(result)
>>> assert all(type(row) is dict for row in result), \
'All elements in result must be dict'
>>> pprint(result)
[{'datetime': datetime.datetime(1969, 7, 14, 21, 0),
'level': 'INFO',
'message': 'Terminal countdown started'},
{'datetime': datetime.datetime(1969, 7, 16, 13, 31, 53),
'level': 'WARNING',
'message': 'S-IC engine ignition (#5)'},
{'datetime': datetime.datetime(1969, 7, 16, 13, 33, 23),
'level': 'DEBUG',
'message': 'Maximum dynamic pressure (735.17 lb/ft^2)'},
{'datetime': datetime.datetime(1969, 7, 16, 13, 34, 44),
'level': 'WARNING',
'message': 'S-II ignition'},
{'datetime': datetime.datetime(1969, 7, 16, 13, 35, 17),
'level': 'DEBUG',
'message': 'Launch escape tower jettisoned'},
{'datetime': datetime.datetime(1969, 7, 16, 13, 39, 40),
'level': 'DEBUG',
'message': 'S-II center engine cutoff'},
{'datetime': datetime.datetime(1969, 7, 16, 16, 22, 13),
'level': 'INFO',
'message': 'Translunar injection'},
{'datetime': datetime.datetime(1969, 7, 16, 16, 56, 3),
'level': 'INFO',
'message': 'CSM docked with LM/S-IVB'},
{'datetime': datetime.datetime(1969, 7, 16, 17, 21, 50),
'level': 'INFO',
'message': 'Lunar orbit insertion ignition'},
{'datetime': datetime.datetime(1969, 7, 16, 21, 43, 36),
'level': 'INFO',
'message': 'Lunar orbit circularization ignition'},
{'datetime': datetime.datetime(1969, 7, 20, 17, 44),
'level': 'INFO',
'message': 'CSM/LM undocked'},
{'datetime': datetime.datetime(1969, 7, 20, 20, 5, 5),
'level': 'WARNING',
'message': 'LM powered descent engine ignition'},
{'datetime': datetime.datetime(1969, 7, 20, 20, 10, 22),
'level': 'ERROR',
'message': 'LM 1202 alarm'},
{'datetime': datetime.datetime(1969, 7, 20, 20, 14, 18),
'level': 'ERROR',
'message': 'LM 1201 alarm'},
{'datetime': datetime.datetime(1969, 7, 20, 20, 17, 39),
'level': 'WARNING',
'message': 'LM lunar landing'},
{'datetime': datetime.datetime(1969, 7, 21, 2, 39, 33),
'level': 'DEBUG',
'message': 'EVA started (hatch open)'},
{'datetime': datetime.datetime(1969, 7, 21, 2, 56, 15),
'level': 'WARNING',
'message': '1st step taken lunar surface (CDR)'},
{'datetime': datetime.datetime(1969, 7, 21, 2, 56, 15),
'level': 'WARNING',
'message': 'Neil Armstrong first words on the Moon'},
{'datetime': datetime.datetime(1969, 7, 21, 3, 5, 58),
'level': 'DEBUG',
'message': 'Contingency sample collection started (CDR)'},
{'datetime': datetime.datetime(1969, 7, 21, 3, 15, 16),
'level': 'INFO',
'message': 'LMP on lunar surface'},
{'datetime': datetime.datetime(1969, 7, 21, 5, 11, 13),
'level': 'DEBUG',
'message': 'EVA ended (hatch closed)'},
{'datetime': datetime.datetime(1969, 7, 21, 17, 54),
'level': 'WARNING',
'message': 'LM lunar liftoff ignition (LM APS)'},
{'datetime': datetime.datetime(1969, 7, 21, 21, 35),
'level': 'INFO',
'message': 'CSM/LM docked'},
{'datetime': datetime.datetime(1969, 7, 22, 4, 55, 42),
'level': 'WARNING',
'message': 'Transearth injection ignition (SPS)'},
{'datetime': datetime.datetime(1969, 7, 24, 16, 21, 12),
'level': 'INFO',
'message': 'CM/SM separation'},
{'datetime': datetime.datetime(1969, 7, 24, 16, 35, 5),
'level': 'WARNING',
'message': 'Entry'},
{'datetime': datetime.datetime(1969, 7, 24, 16, 50, 35),
'level': 'WARNING',
'message': 'Splashdown (went to apex-down)'},
{'datetime': datetime.datetime(1969, 7, 24, 17, 29),
'level': 'INFO',
'message': 'Crew egress'}]
"""
from datetime import date, datetime, time
DATA = """1969-07-14, 21:00:00, INFO, Terminal countdown started
1969-07-16, 13:31:53, WARNING, S-IC engine ignition (#5)
1969-07-16, 13:33:23, DEBUG, Maximum dynamic pressure (735.17 lb/ft^2)
1969-07-16, 13:34:44, WARNING, S-II ignition
1969-07-16, 13:35:17, DEBUG, Launch escape tower jettisoned
1969-07-16, 13:39:40, DEBUG, S-II center engine cutoff
1969-07-16, 16:22:13, INFO, Translunar injection
1969-07-16, 16:56:03, INFO, CSM docked with LM/S-IVB
1969-07-16, 17:21:50, INFO, Lunar orbit insertion ignition
1969-07-16, 21:43:36, INFO, Lunar orbit circularization ignition
1969-07-20, 17:44:00, INFO, CSM/LM undocked
1969-07-20, 20:05:05, WARNING, LM powered descent engine ignition
1969-07-20, 20:10:22, ERROR, LM 1202 alarm
1969-07-20, 20:14:18, ERROR, LM 1201 alarm
1969-07-20, 20:17:39, WARNING, LM lunar landing
1969-07-21, 02:39:33, DEBUG, EVA started (hatch open)
1969-07-21, 02:56:15, WARNING, 1st step taken lunar surface (CDR)
1969-07-21, 02:56:15, WARNING, Neil Armstrong first words on the Moon
1969-07-21, 03:05:58, DEBUG, Contingency sample collection started (CDR)
1969-07-21, 03:15:16, INFO, LMP on lunar surface
1969-07-21, 05:11:13, DEBUG, EVA ended (hatch closed)
1969-07-21, 17:54:00, WARNING, LM lunar liftoff ignition (LM APS)
1969-07-21, 21:35:00, INFO, CSM/LM docked
1969-07-22, 04:55:42, WARNING, Transearth injection ignition (SPS)
1969-07-24, 16:21:12, INFO, CM/SM separation
1969-07-24, 16:35:05, WARNING, Entry
1969-07-24, 16:50:35, WARNING, Splashdown (went to apex-down)
1969-07-24, 17:29, INFO, Crew egress"""
# representation of DATA; dict keys: datetime, level, message
# type: map[dict]
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: Functional Map CSV
# - Difficulty: easy
# - Lines: 5
# - Minutes: 5
# %% English
# 1. Convert `DATA` to `result: map`
# 2. Convert numeric values to `float`
# 3. Run doctests - all must succeed
# %% Polish
# 1. Przekonwertuj `DATA` to `result: map`
# 2. Przekonwertuj wartości numeryczne do `float`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `str.strip()`
# - `str.split()`
# - `map()`
# - `list() + list()`
# - `list.append()`
# - `tuple()`
# %% 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`'
>>> assert type(result) is map, \
'Variable `result` has invalid type, must be a map'
>>> result = list(result) # expand map object
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'
>>> assert all(type(x) is tuple for x in result), \
'All rows in `result` should be tuple'
>>> result # doctest: +NORMALIZE_WHITESPACE
[(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')]
"""
DATA = """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"""
# values from file (note the list[tuple] format!)
# type: map[tuple]
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: Functional Map JSON
# - Difficulty: medium
# - Lines: 6
# - Minutes: 8
# %% English
# 1. Convert from JSON format to Python using decoder function
# 2. Create instances of `Setosa`, `Virginica`, `Versicolor`
# classes based on value in field "species"
# 3. Generate instances in `result: map`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Przekonwertuj dane z JSON do Python używając dekodera funkcyjnego
# 2. Twórz obiekty klas `Setosa`, `Virginica`, `Versicolor`
# w zależności od wartości pola "species"
# 3. Generuj instancje w `result: map`
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `dict.pop()`
# - `globals()[clsname]`
# - `cls(*dict)`
# - `json.loads()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert type(result) is map
>>> result = list(result)
>>> assert len(result) == 9
>>> classes = (Setosa, Virginica, Versicolor)
>>> assert all(type(row) in classes for row in result)
>>> result[0]
Virginica(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9)
>>> result[1]
Setosa(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2)
"""
import json
from dataclasses import dataclass
FILE = r'_temporary.json'
DATA = (
'[{"sepal_length":5.8,"sepal_width":2.7,"petal_length":5.1,"petal_widt'
'h":1.9,"species":"virginica"},{"sepal_length":5.1,"sepal_width":3.5,"'
'petal_length":1.4,"petal_width":0.2,"species":"setosa"},{"sepal_lengt'
'h":5.7,"sepal_width":2.8,"petal_length":4.1,"petal_width":1.3,"specie'
's":"versicolor"},{"sepal_length":6.3,"sepal_width":2.9,"petal_length"'
':5.6,"petal_width":1.8,"species":"virginica"},{"sepal_length":6.4,"se'
'pal_width":3.2,"petal_length":4.5,"petal_width":1.5,"species":"versic'
'olor"},{"sepal_length":4.7,"sepal_width":3.2,"petal_length":1.3,"peta'
'l_width":0.2,"species":"setosa"},{"sepal_length":7.0,"sepal_width":3.'
'2,"petal_length":4.7,"petal_width":1.4,"species":"versicolor"},{"sepa'
'l_length":7.6,"sepal_width":3.0,"petal_length":6.6,"petal_width":2.1,'
'"species":"virginica"},{"sepal_length":4.9,"sepal_width":3.0,"petal_l'
'ength":1.4,"petal_width":0.2,"species":"setosa"}]'
)
@dataclass
class Iris:
sepal_length: float
sepal_width: float
petal_length: float
petal_width: float
class Setosa(Iris):
pass
class Virginica(Iris):
pass
class Versicolor(Iris):
pass
# JSON decoded DATA
# type: map[Iris]
result = ...