Узнайте, как использовать функции map, filter и reduce в Python

В Python есть три функции, которые обеспечивают большую практичность и полезность при программировании. Эти три функции, которые обеспечивают функциональный стиль программирования в объектно-ориентированном языке Python, - это функции map (), filter () и reduce (). Эти функции можно не только использовать по отдельности, но и комбинировать, чтобы обеспечить еще большую полезность.

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

Лямбда-выражения

Лямбда-выражения используются для создания анонимных функций или функций без имени. Они полезны, когда нам нужно создать функцию, которую нужно будет использовать только один раз (функция выброса) и которую можно записать в одну строку. Таким образом, они могут быть очень полезны для использования в функциях, которые принимают другую функцию в качестве аргумента.

Лямбда-функции могут иметь любое количество параметров, но могут иметь только одно выражение. Обычно они имеют такой формат, который дает объект функции:

лямбда-параметры: выражение

Лямбда-выражение с одним параметром
Давайте создадим лямбда-выражение, которое возвращает квадрат числа:

lambda num: num**2

Вот и все! Сначала мы начинаем с ключевого слова лямбда, затем с параметра num, двоеточия и того, что вы хотите вернуть этой функцией, то есть num ** 2.

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

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

square = lambda num: num**2

Затем мы можем вызвать или вызвать эту функцию так же, как с функцией, которая была определена с помощью ключевого слова def. Например:

square(3) # will return 9 as the output

Лямбда-выражение с несколькими параметрами
Давайте напишем лямбда-функцию с двумя параметрами вместо одного. Например, мы можем написать лямбда-выражение, которое возвращает сумму двух чисел:

lambda num1, num2: num1+num2

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

Условные операторы в лямбда-выражениях
Мы также можем включать операторы if else в лямбда-выражения. Нам просто нужно убедиться, что все это в одной строке. Например, давайте создадим функцию, которая принимает два числа и возвращает большее из них:

lambda num1, num2: num1 if num1>num2 else num2

Наше лямбда-выражение принимает два числа, num1 и num2, и возвращает num1, если num1 больше. чем num2, иначе возвращает num2. Очевидно, что эта функция не принимает во внимание, равны ли числа, поскольку в этом случае она просто вернет num2, однако мы просто показываем, как мы можем использовать условные операторы в лямбда-выражении.

Подробнее о лямбда-выражениях:



Теперь, когда мы понимаем, что такое лямбда-функции, давайте поговорим о первой функции, стоящей сегодня на повестке дня: функции карты.

Функция карты

Проще говоря, функция карты предоставляет нам способ применить функцию к каждому элементу в повторяемом объекте, таком как списки, строки, кортежи и т. Д. Таким образом, функция карты принимает два аргумента: функцию, которую мы хотим применить, и итерируемый объект, к которому мы хотим его применить.

карта (функция, итерация)

Например, у нас есть список чисел (повторяемый объект), и мы хотим создать новый список, содержащий квадраты этих чисел.

num_list = [1,2,3,4,5]
squared_list = list(map(lambda x:x**2, num_list))
print(squared_list)
# [1, 4, 9, 16, 25]

Функция карты вернет объект карты, который является итератором. Если мы хотим создать список из этого объекта карты, нам нужно будет передать наш объект карты во встроенную функцию списка.

Что произошло в этой строке кода?

squared_list = list(map(lambda x:x**2, num_list))

Функция map взяла первый элемент из num_list, равный 1, и передала его в качестве аргумента лямбда-функции (поскольку мы передал эту функцию в качестве первого аргумента функции карты). Лямбда-функция приняла этот аргумент, 1, и вернула 1 ** 2 или 1 в квадрате, и это было добавлено к нашему объекту карты. Затем функция карты взяла второй элемент из num_list, равный 2, и передала его в качестве аргумента лямбда-функции. лямбда-функция вернула квадрат 2, который равен 4, который затем был добавлен к нашему объекту карты. После того, как он завершил просмотр элементов num_list и остальные числа в квадрате были добавлены к объекту карты, функция list преобразует этот объект карты в список, и этот список была присвоена переменной squared_list.



Криптография: Цезарь Шифр

Давайте попробуем немного более интересный пример, связанный с криптографией, а именно с шифром Цезаря. Шифр Цезаря шифрует сообщение, беря каждую букву в сообщении и заменяя ее сдвинутой буквой или буквой, которая находится на определенном количестве пробелов в алфавите. Итак, если мы выберем количество пробелов равным 1, то каждая буква будет заменена буквой, расположенной на расстоянии 1 пробела в алфавите. Таким образом, буква a будет заменена буквой b, буква b будет заменена буквой c и так далее. Если мы выберем количество пробелов равным 2, тогда a будет заменено на c, а b будет заменено на d.

Если при подсчете пробелов мы дойдем до конца алфавита, то вернемся к началу алфавита. Другими словами, буква z будет заменена буквой a (если сдвинуть 1 пробел) или буквой b (если сдвинуть 2 пробела).

Например, если сообщение, которое мы хотим зашифровать, - «abc» и мы выбираем количество пробелов равным 1, то зашифрованное сообщение будет «bcd». Если сообщение - «xyz», то зашифрованное сообщение будет «yza».

Как мы можем использовать для этого функцию map ()?
Что ж, мы что-то применяем к каждому элементу повторяемого объекта. В этом случае итерируемый объект является строкой, и мы хотели бы заменить каждую букву / элемент в нашей строке на другую. И функция карты может сделать именно это!

Предположим, что все сообщения будут состоять только из строчных букв. И количество пробелов будет числом от 0 до 26. Помните, мы хотим заменять только буквы другими буквами. Таким образом, любые небуквенные элементы, такие как пробел или символ, останутся без изменений.

Сначала нам нужен доступ к строчному алфавиту. Мы можем либо записать строку со всеми строчными буквами, либо использовать строковый модуль следующим образом:

abc = 'abcdefghijklmnopqrstuvwxyz'
or
import string
abc = string.ascii_lowercase
print(abc)
# 'abcdefghijklmnopqrstuvwxyz'

Затем мы можем написать нашу функцию encrypt следующим образом:

def encrypt(msg, n):
    return ''.join(map(lambda x:abc[(abc.index(x)+n)%26] if x in abc else x, msg))
encrypt('how are you?',2)
# 'jqy ctg aqw?'

Мы создаем функцию encrypt с двумя параметрами: сообщение, которое мы хотим зашифровать, msg, и количество пробелов n, которое мы хотим переместить письма пользователем. Итерируемым объектом, который мы передаем в функцию карты, является сообщение msg. Функция, которую мы передаем в функцию карты, будет лямбда-функцией, которая берет каждый элемент из строки msg, и если этот элемент представляет собой букву алфавита, он заменяет его сдвинутой буквой в зависимости от значения n, которое мы передаем. Это происходит путем взятия текущего индекса этой буквы в алфавите, или abc.index (x), добавляет n, а затем принимает модуль этой суммы. Оператор модуля используется, чтобы начать с начала алфавита, если мы дойдем до конца (если abc.index (x) + n - число больше 25). Другими словами, если исходной буквой была буква z (которая будет иметь индекс 25 в строке abc, которую мы создали выше), а значение n равно 2, то (abc .index (x) + n)% 26 в конечном итоге будет 27% 26, и это даст остаток 1. Таким образом, заменив букву z на букву индекса 1 в алфавите, то есть b.

map(lambda x:abc[(abc.index(x)+n)%26] if x in abc else x,msg)

Помните, что функция карты вернет объект карты. Таким образом, мы можем использовать строковый метод join, который принимает итерируемый объект (которым является объект карты, поскольку он является итератором, и все итераторы являются итеративными), а затем объединяет его в строку. Затем функция возвращает эту строку.

Чтобы расшифровать сообщение, мы можем использовать следующую функцию decrypt (обратите внимание, как мы вычитаем n из abc.index (x) вместо того, чтобы добавлять его):

def decrypt(coded, n):
    return ''.join(map(lambda x:abc[(abc.index(x)-n)%26] if x in abc else x, coded))
decrypt('jqy ctg aqw?',2)
# 'how are you?'


Функция фильтра

Проще говоря, функция фильтра «отфильтрует» повторяемый объект на основе заданного условия. Это условие будет определяться функцией, которую мы передаем.

фильтр (функция, итерация)

Функция фильтр принимает два аргумента: функцию, которая проверяет конкретное условие, и итерацию, к которой мы хотим ее применить (например, список). Функция фильтра берет каждый элемент из нашего списка (или другого итеративного) и передает его функции, которую мы ему даем. Если функция с этим конкретным элементом в качестве аргумента возвращает True, функция фильтра добавит это значение к объекту фильтра (из которого мы можем затем создать список, как мы это сделали с объектом карты выше). Если функция возвращает False, то этот элемент не будет добавлен к нашему объекту фильтра. Другими словами, мы можем думать о функции фильтра как о фильтрации нашего списка или последовательности на основе некоторого условия.

Например, у нас есть список чисел, и мы хотим создать новый список, содержащий только четные числа из нашего списка.

num_list = [1,2,3,4,5,6]
list_of_even_nums = list(filter(lambda x:x%2==0, num_list))
print(list_of_even_nums)
# [2,4,6]

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

Итак, давайте разберемся, что произошло в этой строке кода:

list_of_even_nums = list(filter(lambda x:x%2==0, num_list))

Функция filter взяла первый элемент из num_list, равный 1, и передала его в качестве аргумента лямбда-функции (поскольку мы передал эту функцию в качестве первого аргумента функции фильтра). Затем лямбда-функция возвращает значение False, поскольку 1 не является четным и 1 не добавляется к нашему объекту фильтра. Затем функция фильтра взяла второй элемент из num_list, равный 2, и передала его в качестве аргумента лямбда-функции. лямбда-функция возвращает True, так как 2 - четное число, и поэтому 2 добавляется к нашему объекту фильтра. После прохождения остальных элементов в num_list и добавления остальных четных чисел в наш объект фильтра функция list помещает этот объект фильтра в список, и этот список был назначен переменной list_of_even_nums.



Составление списка против карты и фильтра

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

Пример:

Это понимание списка, которое добавляет квадрат элементов от 0 до 9, только если элемент четный:

[x**2 for x in range(10) if x%2==0]
# [0,4,16,36,64]

Мы можем использовать функции map и filter вместе с лямбда-функциями, чтобы добиться того же:

list(map(lambda x:x**2, filter(lambda x:x%2==0, range(10))))
# [0,4,16,36,64]

Функция, переданная в функцию карты, является лямбда-выражением, которое принимает входной x и возвращает его квадрат. Список, переданный в функцию карты, представляет собой отфильтрованный список, содержащий четные элементы от 0 до 9.

Дополнительные сведения о понимании списков:



Функция уменьшения

Проще говоря, функция сокращения принимает итерацию, такую ​​как список, и сокращает ее до одного совокупного значения. Функция сокращения может принимать три аргумента, два из которых являются обязательными. Двумя обязательными аргументами являются: функция (которая сама принимает два аргумента) и итерируемый (например, список). Третий аргумент, являющийся инициализатором, необязателен.

reduce (функция, итерация [, инициализатор])

Чтобы использовать функцию уменьшения, нам нужно импортировать ее следующим образом:

import functools
or
from functools import reduce

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

Примеры:

Допустим, итерация, которую мы используем, представляет собой список чисел, например num_list:

num_list = [1,2,3,4,5]

Мы хотели бы получить произведение всех чисел в num_list. Сделать это можно следующим образом:

from functools import reduce
product = reduce(lambda x,y:x*y, num_list)
print(product)
# 120

Наш повторяющийся объект - num_list, который представляет собой список: [1,2,3,4,5]. Наша лямбда-функция принимает два аргумента: x и y. Reduce начнет с того, что возьмет первые два элемента num_list, 1 и 2, и передаст их лямбда-функции как x и y аргументы. лямбда функция возвращает их произведение, или 1 * 2, что равно 2. Затем функция Reduce будет использовать это накопленное значение 2 в качестве нового или обновленного x и использует следующий элемент в num_list, равный 3, в качестве нашего нового или обновленного значения y. Затем он отправляет эти два значения (2 и 3) как x и y в нашу лямбда-функцию, которая затем возвращает их произведение 2 * 3. , или 6. Затем это 6 будет использоваться как новое или обновленное значение x, а следующий элемент в num_list будет использоваться как новое или обновленное значение y значение, равное 4. Затем он отправляет 6 и 4 в качестве значений x и y в лямбда-функцию, которая возвращает 24. Таким образом, наше новое значение x равно 24, а новое значение y - это следующий элемент из num_list или 5. Эти два значения передаются в лямбда-функцию как наши значения x и y, и она возвращает их произведение 24 * 5, что равно 120. Таким образом, reduce взял повторяемый объект, в данном случае num_list, и уменьшил его до единственного значения, 120. Это значение затем присваивается переменной produ ct. Другими словами, аргумент x обновляется накопленным значением, а аргумент y обновляется из итерации.

С математической точки зрения вы можете думать о сокращении как о следующем:

f(f(f(f(f(x,y),y),y),y),y)

Таким образом, при первом запуске лямбда-функции она принимает x и y (первые два элемента в списке) и возвращает результат. Этот вывод для f (x, y) будет затем использоваться как новый x для следующей итерации, а y будет следующим элементом в списке, таким образом, f (f (x, y), y). И так далее.

Уменьшить третий аргумент: инициализатор

Помните, как мы говорили, что reduce может принимать необязательный третий аргумент - инициализатор? Значение по умолчанию для него - Нет. Если мы передадим инициализатор, он будет использоваться как первое значение x при помощи reduce (вместо того, чтобы x был первым элементом итерации). Итак, если мы передадим число 2 в приведенном выше примере в качестве инициализатора:

product = reduce(lambda x,y:x*y, num_list, 2)
print(product) 
#240

Тогда первые два значения или аргумента для x и y будут 2 и 1 соответственно. Все последующие шаги будут такими же. Другими словами, инициализатор помещается перед элементами нашей итерации в вычислении.

Другие примеры использования Reduce

Есть так много других сценариев, когда мы можем использовать сокращение.

Например, мы можем найти сумму чисел в списке:

num_list = [1,2,3,4,5]
sum = reduce(lambda x,y: x + y, num_list)
print(sum) 
#15

Или мы можем найти максимальное количество в списке:

num_list = [1,2,3,4,5]
max_num = reduce(lambda x,y: x if x > y else y, num_list)
print(max_num) 
#5

Или минимальное количество в списке:

num_list = [1,2,3,4,5]
min_num = reduce(lambda x,y: x if x < y else y, num_list)
print(min_num) 
#1

И так много других приложений!

Примечание. В Python есть встроенные функции, такие как max (), min () и sum (), которые было бы проще использовать для этих трех примеров. Однако цель состояла в том, чтобы показать, как reduce () можно использовать для выполнения множества различных задач.

Мы также можем комбинировать функцию уменьшения вместе с функцией фильтра или карты. Например, если у нас есть список чисел, но мы хотим вернуть только произведение нечетных чисел, мы можем сделать это с помощью сокращения и фильтрации следующим образом:

num_list = [1,2,3,4,5]
product_of_odd_nums = reduce(lambda x,y: x*y, filter(lambda x:x%2!=0, num_list))
print(product_of_odd_nums) 
# 15 (since 1*3*5=15)

Другими словами, итерация, которую мы передаем функции уменьшения, является объектом фильтра, который включает только нечетные числа. Мы можем думать об этом как о списке только нечетных чисел. Затем функция уменьшения возьмет этот отфильтрованный список и вернет произведение этих чисел.



Заключение

В этом руководстве мы сначала рассмотрели, как создавать лямбда-функции (или анонимные). Затем мы узнали о функции карты, которая дает возможность применить некоторую функцию к каждому элементу в повторяемом объекте. Затем мы рассмотрели функцию фильтрации и то, как ее можно использовать для фильтрации итерируемого объекта на основе некоторого условия. Кроме того, мы сравнили функции карты и фильтра, чтобы составить список. Затем мы рассмотрели, как функцию сокращения можно использовать для уменьшения всего итерируемого объекта до одного значения. Мы также видели примеры использования всех этих функций, включая пример, связанный с криптографией. Наконец, мы узнали, как эти функции могут быть объединены, чтобы обеспечить еще большую функциональность.