Рубрики
Без рубрики

map(), filter () и reduce() в Python с примерами

В этом уроке мы рассмотрим примеры функций map (), filter() и reduce() в Python – как с использованием лямбд, так и обычных функций.

Автор оригинала: Olivera Popović.

Вступление

Функции map() , filter() и reduce() привносят в Python немного функционального программирования. Все три из них являются удобными функциями, которые могут быть заменены List Comprehensions или циклами, но обеспечивают более элегантный и короткий подход к некоторым проблемам.

Прежде чем продолжить, мы рассмотрим несколько вещей, с которыми вы должны быть знакомы, прежде чем читать о вышеупомянутых методах:

Что такое анонимная функция/метод или лямбда?

Анонимный метод-это метод без имени, то есть не привязанный к идентификатору, как при определении метода с помощью defmethod: .

Примечание: Хотя большинство людей используют термины “анонимная функция” и “лямбда – функция” взаимозаменяемо-это не одно и то же. Эта ошибка происходит потому, что в большинстве языков программирования лямбды являются анонимными, а все анонимные функции являются лямбдами. Это также относится и к Python. Таким образом, мы не будем вдаваться в это различие далее в этой статье.

Каков синтаксис лямбда-функции (или лямбда-оператора)?

lambda arguments: expression

Думайте о лямбдах как об однострочных методах без имени. Они работают практически так же, как и любой другой метод в Python, например:

def add(x,y):
	return x + y

Может быть переведен на:

lambda x, y: x + y

Lambdas differ from нормальный Python methods because they can have only one expression, can’t contain any statements and their return type is to function//object. So the line of code above doesn't exactly return the value x + у but the function that calculates x + у//.

Почему лямбды имеют отношение к map() , filter() и reduce() ?

Все три этих метода ожидают в качестве первого аргумента объект function . Этот объект function может быть заранее определенным методом с именем (например, def add(x,y) ).

Хотя чаще всего функции, передаваемые в map () , filter () и reduce () , используются только один раз , поэтому часто нет смысла определять ссылочную функцию.

Чтобы избежать определения новой функции для ваших различных map () /| filter () |/reduce () потребностей – более элегантным решением было бы использовать короткую, одноразовую, анонимную функцию, которую вы будете использовать только один раз и никогда больше – лямбда.

Функция map()

Функция map() перебирает все элементы в данной итерации и выполняет функцию , которую мы передали в качестве аргумента для каждого из них.

Синтаксис таков:

map(function, iterable(s))

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

# Without using lambdas
def starts_with_A(s):
    return s[0] == "A"

fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"]
map_object = map(starts_with_A, fruit)

print(list(map_object))

Этот код приведет к:

[True, False, False, True, False]

Как мы видим, мы получили новый список, в котором функция starts_with_A() была вычислена для каждого из элементов в списке fruit . Результаты этой функции были добавлены в список последовательно.

Более красивый способ сделать то же самое – использовать лямбды:

fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"]
map_object = map(lambda s: s[0] == "A", fruit)

print(list(map_object))

Мы получаем тот же результат:

[True, False, False, True, False]

Примечание: Возможно, вы заметили, что мы бросили map_object в список для печати значения каждого элемента. Мы сделали это потому, что вызов print() в списке выведет фактические значения элементов. Вызов print() on map_object вместо этого выведет адреса памяти значений.

Функция map() возвращает тип map_object , который является итеративным, и мы могли бы также напечатать результаты таким образом:

for value in map_object:
    print(value)

Если вы хотите, чтобы функция map() возвращала список, вы можете просто привести его при вызове функции:

result_list = list(map(lambda s: s[0] == "A", fruit))

Функция filter()

Аналогично map () , filter() берет функцию объект и итерацию и создает новый список.

Как следует из названия, filter() формирует новый список, содержащий только элементы, удовлетворяющие определенному условию, то есть переданная нами функция возвращает True .

Синтаксис таков:

filter(function, iterable(s))

Используя предыдущий пример, мы видим, что новый список будет содержать только те элементы, для которых функция starts_with_A() возвращает True :

# Without using lambdas
def starts_with_A(s):
    return s[0] == "A"

fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"]
filter_object = filter(starts_with_A, fruit)

print(list(filter_object))

Запуск этого кода приведет к более короткому списку:

['Apple', 'Apricot']

Или переписать с помощью лямбды:

fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"]
filter_object = filter(lambda s: s[0] == "A", fruit)

print(list(filter_object))

Печать дает нам тот же результат:

['Apple', 'Apricot']

Функция reduce()

reduce() работает иначе, чем map() и filter() . Он не возвращает новый список, основанный на функции и итерационный, который мы передали. Вместо этого он возвращает одно значение.

Кроме того, в Python 3 reduce() больше не является встроенной функцией, и ее можно найти в модуле functools .

Синтаксис таков:

reduce(function, sequence[, initial])

reduce() работает, вызывая функцию |, которую мы передали для первых двух элементов последовательности. Результат, возвращаемый функцией |, используется в другом вызове функции вместе со следующим (в данном случае третьим) элементом.

Этот процесс повторяется до тех пор, пока мы не пройдем через все элементы последовательности.

Необязательный аргумент initial используется, когда он присутствует, в начале этого “цикла” с первым элементом в первом вызове функции . В некотором смысле элемент initial является 0-м элементом перед первым, когда он указан.

reduce() немного сложнее понять , чем map() и filter () , поэтому давайте рассмотрим пошаговый пример:

  1. Начнем со списка [2, 4, 7, 3] и передайте функцию add(x, y) в reduce() рядом с этим списком, без начального значения

  2. reduce() вызывает add(2, 4) и add() возвращает 6

  3. reduce() вызывает add(6, 7) (результат предыдущего вызова add() и следующего элемента в списке в качестве параметров), и add() возвращает 13

  4. reduce() вызывает add(13, 3) и add() возвращает 16

  5. Поскольку в последовательности больше не осталось элементов, reduce() возвращает 16

Единственное отличие, если бы мы дали начальное значение, было бы дополнительным шагом – 1.5. где reduce() вызовет add(initial, 2) и использует это возвращаемое значение в шаге 2 .

Давайте продолжим и воспользуемся функцией reduce() :

from functools import reduce

def add(x, y):
    return x + y

list = [2, 4, 7, 3]
print(reduce(add, list))

Запуск этого кода приведет к:

16

Опять же, это можно было бы написать с помощью лямбд:

from functools import reduce

list = [2, 4, 7, 3]
print(reduce(lambda x, y: x + y, list))
print("With an initial value: " + str(reduce(lambda x, y: x + y, list, 10)))

И код приведет к тому, что:

16
With an initial value: 26

Вывод

Как уже упоминалось ранее, эти функции являются удобными функциями. Они существуют для того, чтобы вы могли избежать написания более громоздкого кода, но не слишком часто использовать как их, так и лямбда – выражения.

Не форсируйте эти инструменты, потому что “вы можете”, так как это часто может привести к неразборчивому коду, который трудно поддерживать. Используйте их только тогда, когда вам абсолютно ясно, что происходит, как только вы посмотрите на функцию или лямбда-выражение.

Если вы поймаете себя на том, что изо всех сил пытаетесь вписать необходимую логику в одну функцию map() или одно лямбда-выражение, гораздо лучше просто написать немного более длинный метод for-loop/defined и избежать ненужной путаницы позже.