5.1. Loop Comprehension

5.1.1. Recap

>>> result = []
>>>
>>> for x in range(0,5):
...     result.append(x)
>>>
>>> print(result)
[0, 1, 2, 3, 4]

5.1.2. Syntax

result = [<RETURN> for <VARIABLE> in <ITERABLE>]

5.1.3. Example

>>> result = [x for x in range(0,5)]
>>>
>>> print(result)
[0, 1, 2, 3, 4]

5.1.4. Convention

  • Use shorter variable names

  • x is common name

5.1.5. Comprehensions and Generator Expression

  • Comprehensions executes instantly

  • Generator expression executes lazily

List Comprehension:

>>> list(x for x in range(0,5))
[0, 1, 2, 3, 4]
>>>
>>> [x for x in range(0,5)]
[0, 1, 2, 3, 4]

Set Comprehension:

>>> set(x for x in range(0,5))
{0, 1, 2, 3, 4}
>>>
>>> {x for x in range(0,5)}
{0, 1, 2, 3, 4}

Dict Comprehension:

>>> dict((x,x) for x in range(0,5))
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
>>>
>>> {x:x for x in range(0,5)}
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4}

Tuple Comprehension:

>>> tuple(x for x in range(0,5))
(0, 1, 2, 3, 4)

Generator Expression:

>>> (x for x in range(0,5))  # doctest: +ELLIPSIS
<generator object <genexpr> at 0x...>
>>> _ = list(x for x in range(0,5))      # list comprehension
>>> _ = tuple(x for x in range(0,5))     # tuple comprehension
>>> _ = set(x for x in range(0,5))       # set comprehension
>>> _ = dict((x,x) for x in range(0,5))  # dict comprehension
>>> _ = [x for x in range(0,5)]          # list comprehension
>>> _ = (x for x in range(0,5))          # generator expression
>>> _ = {x for x in range(0,5)}          # set comprehension
>>> _ = {x:x for x in range(0,5)}        # dict comprehension

5.1.6. Comprehensions or Generator Expression

>>> data = [x for x in range(0,5)]
>>> list(data)
[0, 1, 2, 3, 4]
>>> print(data)
[0, 1, 2, 3, 4]
>>> data = (x for x in range(0,5))
>>> list(data)
[0, 1, 2, 3, 4]
>>> print(data)  # doctest: +ELLIPSIS
<generator object <genexpr> at 0x...>
>>> from inspect import isgenerator
>>>
>>>
>>> data = [x for x in range(0,5)]
>>> isgenerator(data)
False
>>> from inspect import isgenerator
>>>
>>>
>>> data = (x for x in range(0,5))
>>> isgenerator(data)
True

Comprehension:

>>> data = [x for x in range(0,10)]
>>>
>>> for x in data:  # doctest: +NORMALIZE_WHITESPACE
...     print(x, end=' ')
...     if x == 3:
...         break
0 1 2 3
>>>
>>> for x in data:  # doctest: +NORMALIZE_WHITESPACE
...     print(x, end=' ')
...     if x == 6:
...         break
0 1 2 3 4 5 6
>>>
>>> print(list(data))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> print(list(data))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Generator:

>>> data = (x for x in range(0,10))
>>>
>>> for x in data:  # doctest: +NORMALIZE_WHITESPACE
...     print(x, end=' ')
...     if x == 3:
...         break
0 1 2 3
>>>
>>> for x in data:  # doctest: +NORMALIZE_WHITESPACE
...     print(x, end=' ')
...     if x == 6:
...         break
4 5 6
>>>
>>> print(list(data))
[7, 8, 9]
>>>
>>> print(list(data))
[]

5.1.7. List Comprehension

>>> [x+1 for x in range(0,5)]
[1, 2, 3, 4, 5]
>>>
>>> [x-1 for x in range(0,5)]
[-1, 0, 1, 2, 3]
>>>
>>> [x**2 for x in range(0,5)]
[0, 1, 4, 9, 16]
>>>
>>> [2**x for x in range(0,5)]
[1, 2, 4, 8, 16]
>>> list(x+1 for x in range(0,5))
[1, 2, 3, 4, 5]
>>>
>>> list(x-1 for x in range(0,5))
[-1, 0, 1, 2, 3]
>>>
>>> list(x**2 for x in range(0,5))
[0, 1, 4, 9, 16]
>>>
>>> list(2**x for x in range(0,5))
[1, 2, 4, 8, 16]

5.1.8. Set Comprehension

set comprehension approach to applying function to elements:

>>> {x+10 for x in range(0, 5)}
{10, 11, 12, 13, 14}
>>> set(x+10 for x in range(0, 5))
{10, 11, 12, 13, 14}

5.1.9. Dict Comprehension

dict comprehension approach to applying function to elements:

>>> {x+10:x for x in range(0,5)}
{10: 0, 11: 1, 12: 2, 13: 3, 14: 4}
>>> dict((x+10,x) for x in range(0,5))
{10: 0, 11: 1, 12: 2, 13: 3, 14: 4}

dict comprehension approach to applying function to elements:

>>> {x:x+10 for x in range(0,5)}
{0: 10, 1: 11, 2: 12, 3: 13, 4: 14}
>>> dict((x,x+10) for x in range(0,5))
{0: 10, 1: 11, 2: 12, 3: 13, 4: 14}

dict Comprehension approach to applying function to elements:

>>> {x+10:x+10 for x in range(0,5)}
{10: 10, 11: 11, 12: 12, 13: 13, 14: 14}
>>> dict((x+10,x+10) for x in range(0,5))
{10: 10, 11: 11, 12: 12, 13: 13, 14: 14}

5.1.10. Tuple Comprehension?!

  • Tuple Comprehension vs. Generator Expression

  • More information in Generators

Tuple Comprehension:

>>> tuple(x+10 for x in range(0,5))
(10, 11, 12, 13, 14)

Generator Expression:

>>> (x+10 for x in range(0,5))  # doctest: +ELLIPSIS
<generator object <genexpr> at 0x...>

5.1.11. Map

Applying function to each output element:

>>> [float(x) for x in range(0,5)]
[0.0, 1.0, 2.0, 3.0, 4.0]

Applying function to each output element:

>>> [pow(2,x) for x in range(0,5)]
[1, 2, 4, 8, 16]

Using list comprehension for filtering:

>>> 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')]
>>>
>>> [features for *features,label in DATA]  # doctest: +NORMALIZE_WHITESPACE
[['Sepal length', 'Sepal width', 'Petal length', 'Petal width'],
 [5.8, 2.7, 5.1, 1.9],
 [5.1, 3.5, 1.4, 0.2],
 [5.7, 2.8, 4.1, 1.3],
 [6.3, 2.9, 5.6, 1.8],
 [6.4, 3.2, 4.5, 1.5],
 [4.7, 3.2, 1.3, 0.2],
 [7.0, 3.2, 4.7, 1.4]]
>>> [tuple(features) for *features,label in DATA]  # doctest: +NORMALIZE_WHITESPACE
[('Sepal length', 'Sepal width', 'Petal length', 'Petal width'),
 (5.8, 2.7, 5.1, 1.9),
 (5.1, 3.5, 1.4, 0.2),
 (5.7, 2.8, 4.1, 1.3),
 (6.3, 2.9, 5.6, 1.8),
 (6.4, 3.2, 4.5, 1.5),
 (4.7, 3.2, 1.3, 0.2),
 (7.0, 3.2, 4.7, 1.4)]
>>>
>>> [tuple(X) for *X,y in DATA]  # doctest: +NORMALIZE_WHITESPACE
[('Sepal length', 'Sepal width', 'Petal length', 'Petal width'),
 (5.8, 2.7, 5.1, 1.9),
 (5.1, 3.5, 1.4, 0.2),
 (5.7, 2.8, 4.1, 1.3),
 (6.3, 2.9, 5.6, 1.8),
 (6.4, 3.2, 4.5, 1.5),
 (4.7, 3.2, 1.3, 0.2),
 (7.0, 3.2, 4.7, 1.4)]

5.1.12. Filter

Example 1:

>>> result = []
...
>>> for x in range(0,5):
...     if x % 2 == 0:
...         result.append(x)
>>>
>>> print(result)
[0, 2, 4]
>>> result = [x for x in range(0,5) if x%2==0]
>>> print(result)
[0, 2, 4]

Example 2:

>>> 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')]
>>>
>>> [features for *features,label in DATA if label=='setosa']  # doctest: +NORMALIZE_WHITESPACE
[[5.1, 3.5, 1.4, 0.2],
 [4.7, 3.2, 1.3, 0.2]]
>>>
>>> [X for *X,y in DATA if y=='setosa']  # doctest: +NORMALIZE_WHITESPACE
[[5.1, 3.5, 1.4, 0.2],
 [4.7, 3.2, 1.3, 0.2]]

Using list comprehension for filtering with more complex expression:

>>> 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')]
>>>
>>>
>>> def is_setosa(species):
...     if species == 'setosa':
...         return True
...     else:
...         return False
>>>
>>>
>>> [tuple(X) for *X,y in DATA if is_setosa(y)]  # doctest: +NORMALIZE_WHITESPACE
[(5.1, 3.5, 1.4, 0.2),
 (4.7, 3.2, 1.3, 0.2)]

5.1.13. Nested

>>> DATA = {
...     6: ['Doctorate', 'Prof-school'],
...     5: ['Masters', 'Bachelor', 'Engineer'],
...     4: ['HS-grad'],
...     3: ['Junior High'],
...     2: ['Primary School'],
...     1: ['Kindergarten']}
>>>
>>> result = {}
>>> for i, titles in DATA.items():
...       for title in titles:
...           result[title] = str(i)
>>>
>>> print(result)  # doctest: +NORMALIZE_WHITESPACE
{'Doctorate': '6',
 'Prof-school': '6',
 'Masters': '5',
 'Bachelor': '5',
 'Engineer': '5',
 'HS-grad': '4',
 'Junior High': '3',
 'Primary School': '2',
 'Kindergarten': '1'}
>>>
>>> print(i)
1
>>>
>>> print(title)
Kindergarten
>>>
>>> print(titles)
['Kindergarten']
>>> DATA = {
...     6: ['Doctorate', 'Prof-school'],
...     5: ['Masters', 'Bachelor', 'Engineer'],
...     4: ['HS-grad'],
...     3: ['Junior High'],
...     2: ['Primary School'],
...     1: ['Kindergarten']}
>>>
>>> result = {t: str(i) for i, ts in DATA.items() for t in ts}
>>> result = {title: str(i) for i, titles in DATA.items() for title in titles}
>>>
>>> print(result)  # doctest: +NORMALIZE_WHITESPACE
{'Doctorate': '6',
 'Prof-school': '6',
 'Masters': '5',
 'Bachelor': '5',
 'Engineer': '5',
 'HS-grad': '4',
 'Junior High': '3',
 'Primary School': '2',
 'Kindergarten': '1'}
>>>
>>> # doctest: +SKIP
... print(i)
Traceback (most recent call last):
NameError: name 'i' is not defined
>>>
>>> # doctest: +SKIP
... print(title)
Traceback (most recent call last):
NameError: name 'title' is not defined
>>>
>>> # doctest: +SKIP
... print(titles)
Traceback (most recent call last):
NameError: name 'titles' is not defined

5.1.14. Indent and Whitespaces

>>> result = [pow(x,2) for x in range(0,5)]
>>>
>>> result = [pow(x,2)
...           for x in range(0,5)]
>>> result = [pow(x, 2) for x in range(0, 5) if x % 2 == 0]
>>>
>>> result = [pow(x,2) for x in range(0,5) if x%2==0]
>>> result = [pow(x,2) for x in range(0,5) if x % 2 == 0]
>>>
>>> result = [pow(x,2)
...           for x in range(0,5)
...               if x % 2 == 0]
>>>
>>> result = [pow(x,2)
...           for x in range(0,5)
...           if x % 2 == 0]
>>> DATA = [{'a':1, 'b':2, 'c': 3},
...         {'a':1, 'b':2, 'c': 3},
...         {'a':1, 'b':2, 'c': 3}]
>>>
>>> result = [value for row in DATA for key, value in row.items()]
>>>
>>> result = [value
...           for row in DATA
...             for key, value in row.items()]
>>>
>>> result = [value
...           for row in DATA
...           for key, value in row.items()]
>>>
>>> # doctest: +SKIP
... result = [astronaut | dict(addresses)
...           for astronaut in json.loads(DATA)
...             for i, address in enumerate(astronaut.pop('addresses'), start=1)
...                 if (columns := [f'{key}{i}' for key in address.keys()])
...                     and (addresses := zip(columns, address.values()))]
>>>
>>> # doctest: +SKIP
... result = [astronaut | dict(addresses)
...           for astronaut in json.loads(DATA)
...           for i, address in enumerate(astronaut.pop('addresses'), start=1)
...           if (columns := [f'{key}{i}' for key in address.keys()])
...           and (addresses := zip(columns, address.values()))]

5.1.15. Examples

Increment and decrement:

>>> [x+1 for x in range(0,5)]
[1, 2, 3, 4, 5]
>>> [x-1 for x in range(0,5)]
[-1, 0, 1, 2, 3]

Sum:

>>> sum(x for x in range(0,5))
10
>>> sum(x for x in range(0,5) if x%2==0)
6

Power:

>>> [pow(x,2) for x in range(0,5)]
[0, 1, 4, 9, 16]
>>> [x**2 for x in range(0,5)]
[0, 1, 4, 9, 16]
>>> [pow(2,x) for x in range(0,5)]
[1, 2, 4, 8, 16]
>>> [2**x for x in range(0,5)]
[1, 2, 4, 8, 16]

Even or Odd:

>>> [x for x in range(0,5)]
[0, 1, 2, 3, 4]
>>> [x%2==0 for x in range(0,5)]
[True, False, True, False, True]

Even or Odd:

>>> result = {}
>>>
>>> for x in range(0,5):
...     is_even = (x % 2 == 0)
...     result.update({x: is_even})
>>>
>>> print(result)
{0: True, 1: False, 2: True, 3: False, 4: True}
>>> {x: (x%2==0) for x in range(0,5)}
{0: True, 1: False, 2: True, 3: False, 4: True}

Filtering:

>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> astronauts = [person
...               for person in DATA
...               if person['is_astronaut']]
>>>
>>> print(astronauts)  # doctest: +NORMALIZE_WHITESPACE
[{'is_astronaut': True, 'name': 'Jan Twardowski'},
 {'is_astronaut': True, 'name': 'Mark Watney'},
 {'is_astronaut': True, 'name': 'Melissa Lewis'}]
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> astronauts = [person['name']
...               for person in DATA
...               if person['is_astronaut']]
>>>
>>> print(astronauts)
['Jan Twardowski', 'Mark Watney', 'Melissa Lewis']
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> astronauts = [{'firstname': person['name'].split()[0],
...                'lastname': person['name'].split()[1]}
...                for person in DATA
...                if person['is_astronaut']]
>>>
>>> print(astronauts)  # doctest: +NORMALIZE_WHITESPACE
[{'firstname': 'Jan', 'lastname': 'Twardowski'},
 {'firstname': 'Mark', 'lastname': 'Watney'},
 {'firstname': 'Melissa', 'lastname': 'Lewis'}]
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> astronauts = [{'firstname': person['name'].split()[0].capitalize(),
...                'lastname': person['name'].split()[1][0]+'.'}
...                for person in DATA
...                if person['is_astronaut']]
>>>
>>> print(astronauts)  # doctest: +NORMALIZE_WHITESPACE
[{'firstname': 'Jan', 'lastname': 'T.'},
 {'firstname': 'Mark', 'lastname': 'W.'},
 {'firstname': 'Melissa', 'lastname': 'L.'}]
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> astronauts = [{'firstname': fname, 'lastname': lname}
...                for person in DATA
...                if person['is_astronaut']
...                and (name := person['name'].split())
...                and (fname := name[0].capitalize())
...                and (lname := f'{name[1][0]}.')]
>>>
>>> print(astronauts)  # doctest: +NORMALIZE_WHITESPACE
[{'firstname': 'Jan', 'lastname': 'T.'},
 {'firstname': 'Mark', 'lastname': 'W.'},
 {'firstname': 'Melissa', 'lastname': 'L.'}]
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> astronauts = [f'{fname} {lname[0]}.'
...               for person in DATA
...               if person['is_astronaut']
...               and (fullname := person['name'].split())
...               and (fname := fullname[0].capitalize())
...               and (lname := fullname[1].upper())]
>>>
>>> print(astronauts)
['Jan T.', 'Mark W.', 'Melissa L.']

More information in Assignment Expression

Quick parsing lines:

>>> 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']
>>>
>>> result = []
>>>
>>> for row in DATA:
...     row = row.split(',')
...     result.append(row)
>>>
>>> print(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']]
>>>
>>> [row.split(',') for row in DATA]  # 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']]

Reversing dict keys with values:

>>> DATA = {'a': 1, 'b': 2}
>>>
>>> list(DATA.items())  # doctest: +NORMALIZE_WHITESPACE
[('a', 1),
 ('b', 2)]
>>> [(k,v) for k,v in DATA.items()]  # doctest: +NORMALIZE_WHITESPACE
[('a', 1),
 ('b', 2)]
>>> [(v,k) for k,v in DATA.items()]  # doctest: +NORMALIZE_WHITESPACE
[(1, 'a'),
 (2, 'b')]
>>>
>>> {v:k for k,v in DATA.items()}
{1: 'a', 2: 'b'}

Value collision while reversing dict:

>>> DATA = {'a': 1, 'b': 2, 'c': 2}
>>>
>>> {v:k for k,v in DATA.items()}
{1: 'a', 2: 'c'}

5.1.16. All and Any

>>> all(x for x in range(0,5))
False
>>> any(x for x in range(0,5))
True
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> if all(person['is_astronaut'] for person in DATA):
...     print('Everyone is astronaut')
... else:
...     print('Not everyone is astronaut')
Not everyone is astronaut
>>> DATA = [{'is_astronaut': True,  'name': 'Jan Twardowski'},
...         {'is_astronaut': True,  'name': 'Mark Watney'},
...         {'is_astronaut': False, 'name': 'José Jiménez'},
...         {'is_astronaut': True,  'name': 'Melissa Lewis'},
...         {'is_astronaut': False, 'name': 'Alex Vogel'}]
>>>
>>> if any(person['is_astronaut'] for person in DATA):
...     print('At least one person is astronaut')
... else:
...     print('There are no astronauts')
At least one person is astronaut
>>> 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')]
>>>
>>> all(observation > 1.0
...     for *features, label in DATA[1:]
...     for observation in features
...     if isinstance(observation, float))
False
>>>
>>> all(x > 1.0
...     for *X,y in DATA[1:]
...     for x in X
...     if isinstance(x, float))
False

5.1.17. Conditional Expression

>>> result = ['even' if x % 2 == 0 else 'odd'
...           for x in range(0,10)]
>>> result
['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
>>> result = ['even' if x % 2 == 0 else 'odd'
...           for x in range(0,10)
...           if x % 3 == 0]
>>> result
['even', 'odd', 'even', 'odd']

5.1.18. Assignments

Code 5.51. Solution
"""
* Assignment: Loop Comprehension Create
* Filename: loop_comprehension_create.py
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
    1. Use list comprehension
    2. Generate `result: list[int]` of even numbers from 5 to 20 (without 20)
    3. Compare result with "Tests" section (see below)

Polish:
    1. Użyj rozwinięcia listowego
    2. Wygeneruj `result: list[int]` parzystych liczb z przedziału 5 do 20 (bez 20)
    3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> assert type(result) is list
    >>> assert all(type(x) is int for x in result)
    >>> result
    [6, 8, 10, 12, 14, 16, 18]
"""


# Given
result: list


Code 5.52. Solution
"""
* Assignment: Loop Comprehension Months
* Filename: loop_comprehension_months.py
* Complexity: easy
* Lines of code: 1 lines
* Time: 3 min

English:
    1. Use data from "Given" section (see below)
    2. Use dict comprehension
    3. Convert `MONTH` into dict:
        a. Keys: month number
        b. Values: month name
    4. Month number must be two letter string (zero padded) - `f'{number:02}'`
    5. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Użyj rozwinięcia słownikowego
    3. Przekonwertuj `MONTH` w słownik:
        a. klucz: numer miesiąca
        b. wartość: nazwa miesiąca
    4. Numer miesiąca ma być dwuznakowym stringiem (wypełnij zerem) - `f'{number:02}'`
    5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> type(result)
    <class 'dict'>
    >>> '00' not in result
    True
    >>> '13' not in result
    True
    >>> result['01'] == 'January'
    True
    >>> assert all(type(x) is str for x in result.keys())
    >>> assert all(type(x) is str for x in result.values())
    >>> assert all(len(x) == 2 for x in result.keys())
    >>> result  # doctest: +NORMALIZE_WHITESPACE
    {'01': 'January',
     '02': 'February',
     '03': 'March',
     '04': 'April',
     '05': 'May',
     '06': 'June',
     '07': 'July',
     '08': 'August',
     '09': 'September',
     '10': 'October',
     '11': 'November',
     '12': 'December'}
"""


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

result: dict


Code 5.53. Solution
"""
* Assignment: Loop Comprehension Translate
* Filename: loop_comprehension_translate.py
* Complexity: easy
* Lines of code: 1 lines
* Time: 3 min

English:
    1. Use data from "Given" section (see below)
    2. Define `result: list`
    3. Use list comprehension to iterate over `DATA`
    4. If letter is in `PL` then use conversion value as letter
    5. Add letter to `result`
    6. Redefine `result: str` as a joined `result`
    7. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Zdefiniuj `result: list`
    3. Użyj rozwinięcia listowego do iteracji po `DATA`
    4. Jeżeli litera jest w `PL` to użyj skonwertowanej wartości jako litera
    5. Dodaj literę do `result`
    6. Przedefiniuj `result: str` jako złączony `result`
    7. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> assert type(result) is str
    >>> result
    'zazolc gesla jazn'
"""


# Given
PL = {'ą': 'a', 'ć': 'c', 'ę': 'e',
      'ł': 'l', 'ń': 'n', 'ó': 'o',
      'ś': 's', 'ż': 'z', 'ź': 'z'}

DATA = 'zażółć gęślą jaźń'

result: str


Code 5.54. Solution
"""
* Assignment: Loop Comprehension Split
* Filename: loop_comprehension_split.py
* Complexity: medium
* Lines of code: 9 lines
* Time: 13 min

English:
    1. Use data from "Given" section (see below)
    2. Calculate pivot point: length of data times given percent (60%/40%, see below)
    3. Using List Comprehension split data to:
        a. `features: list[tuple]` - list of measurements (each measurement row is a tuple)
        b. `labels: list[str]` - list of species names
    4. Split those data structures with proportion:
        a. `features_train: list[tuple]` - features to train - 60%
        b. `features_test: list[tuple]` - features to test - 40%
        c. `labels_train: list[str]` - labels to train - 60%
        d. `labels_test: list[str]` - labels to test - 40%
    5. Compare results with "Tests" section below

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Wylicz punkt podziału: długość danych razy zadany procent (60%/40%, patrz poniżej)
    3. Używając List Comprehension podziel dane na:
        a. `features: list[tuple]` - lista pomiarów (każdy wiersz z pomiarami ma być tuple)
        b. `labels: list[str]` - lista nazw gatunków
    4. Podziel te struktury danych w proporcji:
        a. `features_train: list[tuple]` - features do uczenia - 60%
        b. `features_test: list[tuple]` - features do testów - 40%
        c. `labels_train: list[str]` - labels do uczenia - 60%
        d. `labels_test: list[str]` - labels do testów - 40%
    5. Porównaj wynik z sekcją "Tests" poniżej

Tests:
    >>> assert type(features_train) is list
    >>> assert type(features_test) is list
    >>> assert type(labels_train) is list
    >>> assert type(labels_test) is list
    >>> assert all(type(x) is tuple for x in features_train), 'features_train: expected type list[tuple]'
    >>> assert all(type(x) is tuple for x in features_test), 'features_test: expected type list[tuple]'
    >>> assert all(type(x) is str for x in labels_train)
    >>> assert all(type(x) is str for x in labels_test)
    >>> features_train  # doctest: +NORMALIZE_WHITESPACE
    [(5.8, 2.7, 5.1, 1.9),
     (5.1, 3.5, 1.4, 0.2),
     (5.7, 2.8, 4.1, 1.3),
     (6.3, 2.9, 5.6, 1.8),
     (6.4, 3.2, 4.5, 1.5),
     (4.7, 3.2, 1.3, 0.2)]
    >>> features_test  # doctest: +NORMALIZE_WHITESPACE
    [(7.0, 3.2, 4.7, 1.4),
     (7.6, 3.0, 6.6, 2.1),
     (4.9, 3.0, 1.4, 0.2),
     (4.9, 2.5, 4.5, 1.7)]
    >>> labels_train
    ['virginica', 'setosa', 'versicolor', 'virginica', 'versicolor', 'setosa']
    >>> labels_test
    ['versicolor', 'virginica', 'setosa', 'virginica']
"""


# Given
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.9, 3.0, 1.4, 0.2, 'setosa'),
        (4.9, 2.5, 4.5, 1.7, 'virginica')]

features_train: list
features_test: list
labels_train: list
labels_test: list