четверг, 2 января 2020 г.

ЛМНты Python, 36 - 40

>>> Две или больше

Функции map можно подать на вход больше одной последовательности и обработать их параллельно. Например, просуммировать поэлементно две последовательности:

>>> s1 = [1, 2, 3]
>>> s2 = [4, 5, 6, 7]
>>> m = map(lambda x, y: x+y, s1, s2)
>>> list(m)
[5, 7, 9]

Обработка завершается, когда достигнут конец самой короткой последовательности. Так же ведет себя функция zip, собирающая из нескольких последовательностей одну – с кортежами:

>>> z = zip(s1, range(5))
>>> list(z)
[(1, 0), (2, 1), (3, 2)]
>>> m = map(lambda x, y: (x, y), s1, range(5))
>>> list(m)
[(1, 0), (2, 1), (3, 2)]

>>> reduce может

Если при вызове filter вместо функции передано значение None, то результирующая последовательность будет состоять из элементов исходной, оцененных как True.

>>> seq = [1, 0, 3, 0]
>>> f = filter(None, seq)
>>> list(f)
[1, 3]

Функцию reduce (неожиданно) можно использовать для порождения последовательности, если аккумулятороом будет последовательность:

>>> from functools import reduce
>>> reduce(lambda a, x: a + [x**3], seq, [])
[1, 0, 27, 0]
>>> reduce(lambda a, x: a + [x] if x else a, seq, [])
[1, 3]

Первый пример имитирует работу map, второй – работу filter.

>>> Списковое включение

Списковое включение (list comprehension) – замечательная конструкция Python, позволяющая из последовательности или итератора построить новый список; при этом возможно как преобразование исходных значений элементов (аналог map), так и фильтрация (аналог filter). Пример:

>>> seq = [1, 2, 3, 4]
>>> [x**2 for x in seq]
[1, 4, 9, 16]

Здесь часть for x in seq организует обход всех элементов исходной последовательности, а выражение x**2 в цикле вычисляет значение очередного элемента результирующего списка. Приведенный пример соответствует следующему фрагменту кода:

>>> res = []
>>> for x in seq:
...     res.append(x**2)
...
>>> res
[1, 4, 9, 16]

Опциональная часть if позволяет организовать фильтрацию элементов исходной последовательности:

>>> [x for x in seq if x%2]
[1, 3]
>>> [x**2 for x in seq if x%2]
[1, 9]

>>> Вложенные списковые включения

Итерации и фильтры в списковом включении могут быть вложенными, что позволяет, в частности, обрабатывать многомерные структуры данных:

>>> seq = [range(3), range(3), range(3)]
>>> [e for r in seq for e in r]
[0, 1, 2, 0, 1, 2, 0, 1, 2]

Приведенный пример соответствует следующему фрагменту кода:

>>> res = []
>>> for r in seq:
...     for e in r:
...         res.append(e)
...
>>> res
[0, 1, 2, 0, 1, 2, 0, 1, 2]

Получим декартово произведение последовательностей; отфильтуем элементы исходных последовательностей:

>>> alp = ['a', 'b', 'c']
>>> num = [101, 102]
>>> [(a, n) for a in alp for n in num]
[('a', 101), ('a', 102), ('b', 101), ('b', 102), ('c', 101), ('c', 102)]
>>> [(a, n) for a in alp for n in num if a != 'a' if n%2]
[('b', 101), ('c', 101)]

>>> Генераторное выражение

Генераторное выражение (generator expression) умеет все, что умеет списковое включение, но результатом его выполнения является не список, а объект-генератор. Генераторное выражение заключается в обычные скобки:

>>> g = (x**2 for x in range(6) if x%2)
>>> type(g)
<class 'generator'>
>>> g
<generator object <genexpr> at 0x0000020DAC65C308>

Генератор можно использовать в тех же контекстах, что и итератор, но нужно иметь в виду, что генератор является одноразовым и "расходуется" при использовании:

>>> for x in g:
...     print(x)
...
1
9
25
>>> for x in g:
...     print(x)
...
>>>

При передаче генераторного выражения в функцию в качeстве аргумента дублировать скобки не требуется:

>>> list(x**2 for x in range(6))
[0, 1, 4, 9, 16, 25]

Комментариев нет:

Отправить комментарий