0 знаков
53. Функциональное программирование (map, filter и reduce)
Кратко- Python поддерживает функциональную парадигму программирования.
- Функциональное программирование основано на вычислении значений функций в математическом понимании.
- Функция
map()
применяет функции к элементам итерируемого объекта.- Функция
filter()
фильтрует элементы на основе проверяемой функции.- Функция
reduce()
применяет функции к парам элементов и результатов выполнения.- Лямбда-выражения, замыкания и списковые включения тоже используются в функциональном программировании.
- Генераторные выражения - это объекты-генераторы, которые возвращают значения.
- Функция
filter()
требует, чтобы проверочная функция возвращала логическое значение.- Функция
reduce()
возвращает одиночный результат, применяя функцию к каждому элементу итерируемого объекта.
Введение
В предыдущем уроке мы разобрались с выражением lambda
и, как поймете из этого урока, совсем не случайно. Язык Python поддерживает множество парадигм: процедурную (при помощи базовых операторов), объектно-ориентированную (с помощью классов) и функциональную. О последней сейчас и поговорим. Функциональная парадигма включает в себя набор встроенных функций, а именно: map()
– вызывает функции на элементах итерируемого объекта, filter()
– фильтрует элементы на основе проверяемой функции и reduce()
– применяет функции к парам элементов и результатов выполнения. Да, и конечно про лямбда-выражения, замыкания и списковые включения не будем забывать – все нам пригодится.
Функциональное программирование – парадигма, в которой процесс вычисления понимается как вычисление значений функций в математическом понимании.
Функция map()
Какое действие мы чаще всего осуществляем со списками? Верно, применение какой-то операции к каждому элементу списка. Давайте выполним простое действие, прибавим к каждому элементу списка единицу.
my_numbers = [1, 2, 3, 4]
my_numbers_inc = []
for item in my_numbers:
item = item + 1
my_numbers_inc.append(item)
print(my_numbers_inc) # => [2, 3, 4, 5]
Оставим этот пример и перейдем к синтаксису функции map()
:
map(func, iterable, [iterable 2, iterable 3, ...])
Где func
обозначает функцию, которая будет применена к каждому элементу из iterables
. Количество аргументов функции должно совпадать с количеством iterables
. Чтобы было понятно, перепишем предыдущий пример:
results = list(map(lambda x: x + 1, my_numbers))
print(results) # => [2, 3, 4, 5]
Как видите, реализация получилось более компактной.
В языке Python есть функция zip()
, которая объединяет элементы из нескольких итерируемых объектов. Предположим, у нас есть два списка: имен и каких-то чисел, например, возрастов.
names = ["Иван", "Петр", "Ирина"]
age = [20, 25, 18, 33]
print(list(zip(names, age))) # => [('Иван', 20), ('Петр', 25), ('Ирина', 18)]
Сразу обратим внимание на вывод и что происходит, когда списки содержат разное количество элементов. Теперь попробуем реализовать встроенную функцию zip()
, используя полученные знания.
result = list(map(lambda x, y: (x, y), names, age))
print(result) # => [('Иван', 20), ('Петр', 25), ('Ирина', 18)]
Этот код нисколько не проще. Этим хотел показать насколько гибкий и удобный инструмент мы сейчас изучаем и как его можно применять. Кстати, реальная реализация функции zip()
немного другая:
def zip(*iterables):
sentinel = object()
iterators = [iter(it) for it in iterables]
while iterators:
result = []
for it in iterators:
elem = next(it, sentinel)
if elem is sentinel:
return
result.append(elem)
yield tuple(result)
Многое в этом коде вам знакомо: структуры, итераторы, условные выражения, циклы и другое. Но пока вы не знаете о функции object()
и операторе yield
. О последнем будет в следующем уроке.
Что еще можно добавить про функцию map()
– это то, что она возвращает. Возможно, об этом нужно было написать до конкретных примеров, но лучше поздно, чем никогда. На самом деле это было сделано сознательно, чтобы вы на практике поняли что возвращает map()
. И это не список (как в Python 2), это нечто другое – объект-генератор. Об этом не было в уроке «List/dict/set comprehensions (включения) в Python» и здесь не будет. Про генераторные выражения, они же generator expression читайте в следующих уроках.
Вернемся к нашей теме и переходим к следующей функции, на которой строится парадигма функционального программирования.
Функция filter()
Название функции filter()
отвечает само за себя. В отличие от map()
, которая пропускает каждый элемент через функцию и возвращает результат, filter()
требует, чтобы проверочная функция (та, что указана в первом параметре filter()
) возвращала логическое значение и таким образом «отфильтровывала» ложные элементы. Синтаксис похож на map()
, поэтому сразу рассмотрим пример:
age = [20, 15, 18, 33]
result = list(filter(lambda x: x > 18, age))
print(result) # => [20, 33]
В этом примере мы оставляем числа строго больше 18
. Усложним пример и добавим список людей. Объединим людей и их возраст. В итоге оставим список совершеннолетних.
names = ["Иван", "Петр", "Ирина", "Николай"]
age = [20, 15, 18, 33]
result = list(map(lambda x, y: (x, y), names, age))
result = list(filter(lambda x: x[1] >= 18, result))
print(result) # => [('Иван', 20), ('Ирина', 18), ('Николай', 33)]
Обратите внимание, как мы проверяем возраст: x[1] >= 18
. Эту задачу можно решить при помощи списковых включений (list comprehensions) следующим образом:
names = ["Иван", "Петр", "Ирина", "Николай"]
age = [20, 15, 18, 33]
result = list(map(lambda x, y: (x, y), names, age))
result = [x for x in result if x[1] >= 18]
print(result) # => [('Иван', 20), ('Ирина', 18), ('Николай', 33)]
Или такой вариант:
names = ["Иван", "Петр", "Ирина", "Николай"]
age = [20, 15, 18, 33]
result = list(map(lambda x, y: (x, y), names, age))
result = (x for x in result if x[1] >= 18)
print(list(result)) # => [('Иван', 20), ('Ирина', 18), ('Николай', 33)]
Изменились скобки и теперь в переменной result
хранится объект-генератор. В таком случае память выделяется только при вызове функции list()
, когда мы выводим список. Снова забежал вперед. Скоро дойдем до этой темы, а пока возвращаемся к функции filter()
.
Как вы уже догадались, функции filter()
, в отличие от map()
, необходим только один итерируемый аргумент. Первый параметр необходим для возврата логического типа, о чем было выше, а так как итерируемый объект всего один, то и принимать наша функция (первый параметр) должна только один аргумент. Если вы запутались в словах аргумент и параметр, то вернитесь к уроку «Аргументы и параметры функций, операторы * и ** в Python». И самое очевидное, пропускаться через проверочную функцию будут только те элементы, которые имеют значение true
. Плавно переходим к следующей функции - reduce()
.
Функция reduce()
В Python 3.X функция reduce()
не является встроенной, а расположена в модуле functools
. Выглядит она сложнее предыдущих двух, поэтому давайте разбираться. Она принимает итерируемый объект, а возвращает одиночный результат. Взглянем на синтаксис:
reduce(func, iterable[, initial])
Параметр func
– функция, к которой кумулятивно применяется каждый элемент iterable
. Значение initial
является необязательным и в случае указания, служит стартовым значением или значением по умолчанию, если итерируемый объект пуст.
Функция func
требует два аргумента (или один, если указан initial
). Разберем на примере. Напишем программу, которая считает сумму чисел в списке.
from functools import reduce
numbers = [1, 2, 3, 4, 5, 6]
result = reduce(lambda x, y : x + y, numbers)
print(result) # => 21
Не забываем подключить модуль functools
и импортировать из нее reduce()
. Думаю, очевидно, что произойдет, если в параметр initial
передать число 10
.
from functools import reduce
numbers = [1, 2, 3, 4, 5, 6]
result = reduce(lambda x, y : x + y, numbers, 10)
print(result) # => 31
На этом все. В этом уроке вы познакомились с парадигмой функционального программирования, а именно с функциями map()
, filter()
и reduce()
, поняли, для чего мы проходили лямбда-выражения, вспомнили списковые включения и плавно подошли к теме «генераторные выражения».
Тест
Похожие уроки Codebra
Подписывайся на наш Telegram-канал!
Новости, полезный материал,
программирование и ИБ
Подписывайся на наш Telegram-канал!
Новости, полезный материал,
программирование и ИБ