# 11.6. Function Generator-Like¶

• It is not a generator

• Generator-like objects

• range(start, stop, step)

• reversed(iterable)

• enumerate(iterable, start=0)

• zip(*iterable, strict=False)

• map(func, iterable)

• filter(func, iterable)

• next(iterable)

• iter(iterable)

## 11.6.1. Range¶

• range([start], <stop>, [step])

• optional start, inclusive, default: 0

• required stop, exclusive

• optional step, default: 1

range() syntax:

>>> range(0,3)
range(0, 3)

>>> list(range(0,3))
[0, 1, 2]
>>>
>>> tuple(range(0,3))
(0, 1, 2)
>>>
>>> set(range(0,3))
{0, 1, 2}
>>>
>>> list(range(4,11,2))
[4, 6, 8, 10]


## 11.6.2. Reversed¶

• reversed() - Return a reverse iterator over the values of the given sequence

>>> data = (1, 2, 3)
>>> list(reversed(data))
[3, 2, 1]


## 11.6.3. Enumerate¶

• enumerate(*iterables)

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months)
>>>
>>> list(result)
[(0, 'January'), (1, 'February'), (2, 'March')]

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months)
>>>
>>> dict(result)
{0: 'January', 1: 'February', 2: 'March'}

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months, start=1)
>>>
>>> dict(result)
{1: 'January', 2: 'February', 3: 'March'}

>>> months = ['January', 'February', 'March']
>>> result = {f'{i:02}':month for i,month in enumerate(months, start=1)}
>>>
>>> print(result)
{'01': 'January', '02': 'February', '03': 'March'}


## 11.6.4. Zip¶

• zip(*iterables)

>>> firstnames = ['Mark', 'Melissa', 'Rick']
>>> lastnames = ['Watney', 'Lewis', 'Martinez']
>>>
>>> list(zip(firstnames, lastnames))
[('Mark', 'Watney'), ('Melissa', 'Lewis'), ('Rick', 'Martinez')]

>>> firstnames = ['Mark', 'Melissa', 'Rick']
>>> lastnames = ['Watney', 'Lewis', 'Martinez']
>>>
>>> dict(zip(firstnames, lastnames))
{'Mark': 'Watney', 'Melissa': 'Lewis', 'Rick': 'Martinez'}

>>> roles = ['botanist', 'commander', 'pilot']
>>> names = ['Mark Watney', 'Melissa Lewis', 'Rick Martinez']
>>>
>>> dict(zip(roles, names))
{'botanist': 'Mark Watney',
'commander': 'Melissa Lewis',
'pilot': 'Rick Martinez'}


zip() adjusts to the shortest:

>>> firstnames = ['Mark', 'Melissa']
>>> lastnames = ['Watney', 'Lewis', 'Martinez']
>>>
>>> list(zip(firstnames, lastnames))
[('Mark', 'Watney'), ('Melissa', 'Lewis')]


Three-way:

>>> roles = ['botanist', 'commander', 'pilot']
>>> firstnames = ['Mark', 'Melissa', 'Rick']
>>> lastnames = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(roles, firstnames, lastnames)
>>>
>>>
>>> next(result)
('botanist', 'Mark', 'Watney')
>>>
>>> next(result)
('commander', 'Melissa', 'Lewis')
>>>
>>> next(result)
('pilot', 'Rick', 'Martinez')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration


## 11.6.5. Map¶

• Apply function to all elements of data

• map(callable, *iterables)

>>> def square(x):
...     return x ** 2

>>> data = [1, 2, 3]
>>> result = map(square, data)
>>> list(result)
[1, 4, 9]


## 11.6.6. Filter¶

• filter(callable, *iterables)

>>> def even(x):
...     return x % 2 == 0

>>> data = [0, 1, 2, 3, 4]
>>> result = filter(even, data)
>>> list (result)
[0, 2, 4]


## 11.6.7. Next¶

Range:

>>> result = range(0,5)
>>>
>>> next(result)
Traceback (most recent call last):
TypeError: 'range' object is not an iterator


Zip two-way:

>>> firstnames = ['Mark', 'Melissa', 'Rick']
>>> lastnames = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(firstnames, lastnames)
>>>
>>>
>>> next(result)
('Mark', 'Watney')
>>>
>>> next(result)
('Melissa', 'Lewis')
>>>
>>> next(result)
('Rick', 'Martinez')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration


Enumerate:

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months)
>>>
>>> next(result)
(0, 'January')
>>>
>>> next(result)
(1, 'February')
>>>
>>> next(result)
(2, 'March')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration


Zip n-way:

>>> roles = ['botanist', 'commander', 'pilot']
>>> firstnames = ['Mark', 'Melissa', 'Rick']
>>> lastnames = ['Watney', 'Lewis', 'Martinez']
>>>
>>> result = zip(roles, firstnames, lastnames)
>>>
>>>
>>> next(result)
('botanist', 'Mark', 'Watney')
>>>
>>> next(result)
('commander', 'Melissa', 'Lewis')
>>>
>>> next(result)
('pilot', 'Rick', 'Martinez')
>>>
>>> next(result)
Traceback (most recent call last):
StopIteration


Map:

>>> def square(x):
...     return x ** 2
>>>
>>> data = [1, 2, 3]
>>> result = map(square, data)
>>>
>>>
>>> next(result)
1
>>>
>>> next(result)
4
>>>
>>> next(result)
9
>>> next(result)
Traceback (most recent call last):
StopIteration


Filter:

>>> def even(x):
...     return x % 2 == 0
>>>
>>> data = [0, 1, 2, 3, 4]
>>> result = filter(even, data)
>>>
>>>
>>> next(result)
0
>>>
>>> next(result)
2
>>>
>>> next(result)
4
>>> next(result)
Traceback (most recent call last):
StopIteration


## 11.6.8. Iter¶

Range:

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


Enumerate:

>>> months = ['January', 'February', 'March']
>>>
>>> for i, month in enumerate(months, start=1):
...     print(f'{i=}, {month=}')
i=1, month='January'
i=2, month='February'
i=3, month='March'


Zip:

>>> roles = ['botanist', 'commander', 'pilot']
>>> names = ['Mark Watney', 'Melissa Lewis', 'Rick Martinez']
>>>
>>> for role, name in zip(roles, names):
...     print(f'{role=}, {name=}')
role='botanist', name='Mark Watney'
role='commander', name='Melissa Lewis'
role='pilot', name='Rick Martinez'


## 11.6.9. Generator Chain¶

• Function composition

>>> def square(x):
...     return x ** 2
>>>
>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> result = range(0,10)
>>> result = map(square, result)
>>> result = filter(even, result)
>>>
>>> for value in result:
...     print(value)
...     if value > 3:
...         break
0
4
>>>
>>> next(result)
16
>>>
>>> list(result)
[36, 64]


## 11.6.10. Itertools¶

• itertools.count(start=0, step=1)

• itertools.cycle(iterable)

• itertools.repeat(object[, times])

• itertools.accumulate(iterable[, func, *, initial=None])

• itertools.chain(*iterables)

• itertools.compress(data, selectors)

• itertools.islice(iterable, start, stop[, step])

• itertools.starmap(function, iterable)

• itertools.product(*iterables, repeat=1)

• itertools.permutations(iterable, r=None)

• itertools.combinations(iterable, r)

• itertools.combinations_with_replacement(iterable, r)

• itertools.groupby(iterable, key=None)

## 11.6.11. Use Case - 0x01¶

>>> def increment(x):
...     return x + 1
>>>
>>>
>>> data = [1, 2, 3, 4]
>>> result = map(increment, data)
>>>
>>> list(result)
[2, 3, 4, 5]


## 11.6.12. Use Case - 0x02¶

>>> def square(x):
...     return x ** 2
>>>
>>>
>>> data = [1, 2, 3]
>>> result = map(square, data)
>>>
>>> list(result)
[1, 4, 9]


## 11.6.13. Use Case - 0x03¶

>>> PL = {'ą': 'a', 'ć': 'c', 'ę': 'e',
...       'ł': 'l', 'ń': 'n', 'ó': 'o',
...       'ś': 's', 'ż': 'z', 'ź': 'z'}
>>>
>>>
>>> def translate(letter):
...     return PL.get(letter, letter)
>>>
>>>
>>> text = 'zażółć gęślą jaźń'
>>> result = map(translate, text)
>>>
>>> ''.join(result)
'zazolc gesla jazn'


## 11.6.14. Use Case - 0x04¶

>>> people = [
...     {'age': 21, 'name': 'Mark Watney'},
...     {'age': 25, 'name': 'Melissa Lewis'},
...     {'age': 18, 'name': 'Rick Martinez'}]
>>>
>>>
...     return person['age'] >= 21
>>>
>>>
>>> result = filter(adult, people)
>>>
>>> list(result)
[{'age': 21, 'name': 'Mark Watney'},
{'age': 25, 'name': 'Melissa Lewis'}]


## 11.6.15. Use Case - 0x05¶

>>> people = [
...     {'is_astronaut': False, 'name': 'Mark Watney'},
...     {'is_astronaut': True, 'name': 'Melissa Lewis'},
...     {'is_astronaut': True, 'name': 'Rick Martinez'}]
>>>
>>>
>>> def astronaut(person):
...     return person['is_astronaut']
>>>
>>>
>>> result = filter(astronaut, people)
>>>
>>> list(result)
[{'is_astronaut': True, 'name': 'Melissa Lewis'},
{'is_astronaut': True, 'name': 'Rick Martinez'}]


## 11.6.16. Use Case - 0x06¶

Sum stdin (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.6.17. Assignments¶

"""
* Assignment: Function Generator Map
* Required: yes
* Complexity: easy
* Lines of code: 3 lines
* Time: 3 min

English:
1. Define function cube():
a. takes one argument
b. returns its argument cubed (raised to the power of 3)
2. Use map() to apply function cube() to DATA
3. Define result: list[int] with evaluated result
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj funckję cube():
a. przyjmuje jeden argument
b. zwraca argument podniesiony do sześcianu (do 3 potęgi)
2. Użyj map() zaaplikować funkcję cube() do DATA
3. Zdefiniuj result: list[int] z ewaluaownym wynikiem
4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
* map()
* list()

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> 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 list, \
'Variable result has invalid type, should be list'
>>> assert all(type(x) is int for x in result), \
'All rows in result should be int'

>>> result
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
"""

DATA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Returns its argument cubed (raised to the power of 3)
# type: Callable[[int], int]
def cube():
...

# Cube numbers in DATA
# type: list[float]
result = ...


"""
* Assignment: Function Generator Filter
* Required: yes
* Complexity: easy
* Lines of code: 3 lines
* Time: 3 min

English:
1. Define function odd():
a. takes one argument
b. returns True if argument is odd
c. returns False if argument is even
2. Use filter() to apply function odd() to DATA
3. Define result: list[int] with evaluated result
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj funckję odd():
a. przyjmuje jeden argument
b. zwraca True jeżeli argument jest nieparzysty
c. zwraca False jeżeli argument jest parzysty
2. Użyj filter() zaaplikować funkcję odd() do DATA
3. Zdefiniuj result: list[int] z ewaluaownym wynikiem
4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
* filter()
* list()

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert isfunction(odd), \
'Object odd must be a function'

>>> assert result is not Ellipsis, \
'Assign 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 rows in result should be int'

>>> result
[1, 3, 5, 7, 9]
"""

DATA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Returns its argument cubed (raised to the power of 3)
# type: Callable[[int], int]
def odd():
...

# Cube numbers in DATA
# type: list[float]
result = ...


"""
* Assignment: Function Generator Chain
* Required: yes
* Complexity: easy
* Lines of code: 4 lines
* Time: 3 min

English:
1. Use range() to get numbers:
a. from 0 (inclusive)
b. to 10 (exclusive)
2. Redefine result with odd numbers from result
3. Redefine result with cubed numbers from result
4. Redefine result with evaluated result
5. Run doctests - all must succeed

Polish:
1. Użyj range() aby otrzymać liczby:
a. od 0 (włącznie)
b. do 10 (rozłącznie)
2. Przedefiniuj result z nieparzystymi liczbami z result
3. Przedefiniuj result z podniesionymi do sześcianiu liczbami z result
4. Przedefiniuj result z ewaluaownym result
5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
* range()
* map()
* filter()
* list()

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert isfunction(odd), \
'Object odd must be a function'
>>> assert isfunction(cube), \
'Object cube must be a function'
>>> assert result is not Ellipsis, \
'Assign 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 rows in result should be int'

>>> result
[1, 27, 125, 343, 729]
"""

def odd(x):
return x % 2

def cube(x):
return x ** 3

# Range from 0 to 10 (exclusive)
# type: Iterator[int]
result = ...

# Filter odd numbers
# type: Iterator[int]
result = ...

# Cube result
# type: Iterator[int]
result = ...

# Get list of results
# type: list[int]
result = ...


"""
* Assignment: Function Generator Chain
* Required: yes
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
1. Define result: float with arithmetic mean of DATA
2. Note, that all the time you are working on a data stream
3. Run doctests - all must succeed

Polish:
1. Zdefiniuj result: float ze średnią arytmetyczną z DATA
2. Zwróć uwagę, że cały czas pracujesz na strumieniu danych
3. Uruchom doctesty - wszystkie muszą się powieść

Hints:
* type cast to list() to expand generator before calculating mean
* mean = sum(...) / len(...)
* TypeError: object of type 'map' has no len()
* ZeroDivisionError: division by zero

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert isfunction(odd), \
'Object odd must be a function'
>>> assert isfunction(cube), \
'Object cube must be a function'
>>> assert result is not Ellipsis, \
'Assign result to variable: result'
>>> assert type(result) is float, \
'Variable result has invalid type, should be float'

>>> result
245.0
"""

def odd(x):
return x % 2

def cube(x):
return x ** 3

DATA = range(0, 10)
DATA = filter(odd, DATA)
DATA = map(cube, DATA)

# Calculate mean of DATA
# type: float
result = ...