Вы уверены, что хотите покинуть сраницу?
Не сохранные данные будет утеряны!
В этой статье нами будут рассмотрены различные аспекты использования таких довольно специфических внутренних функций Python, как map() и filter(). Данные функции относятся к так называемым ФВП функциям высшего порядка, основная особенность которых заключается в их способности оперировать другими функциями или, если быть точнее – эти функции должны либо принимать иные функции в качестве аргумента, либо возвращать их, как результат. В отдельных случаях, вышеназванные ФВП функции выполняют оба эти действия.
Примечание: В Python существует мощнейшая и гораздо более продвинутая альтернатива функциям высшего порядка - так называемые декораторы, которые позволяют превращать в ФВП практически любые библиотечные и пользовательские функции и методы данного языка. Более подробно узнать о декораторах и окунуться в завораживающий мир их использования можно из статьи от нашей Онлайн школы обучения профессиональному программированию - PYLOT, называющейся Декораторы, как средство для самосовершенствования кода в Python.
Принцип работы, рассматриваемых нами в этой статье, функций map() и filter() абсолютно аналогичен тому, по которому работают все остальные ФВП, но в то же время, наши функции имеют и некую свою особенность, позволяющую упростить и сократить код, применяя их к множеству итераций в процессе выполнения одного полного цикла.
Принцип работы функции map(), а также ее особенности, лучше всего рассмотреть на практике, выполнив простой пример. Давайте напишем код, в котором наша функция map() примет в качестве аргумента другую функцию, а также итерируемый объект в виде определенной последовательности данных. Допустим, это будет функция kvadrat() и список chisla:
def kvadrat(n):
return n ** 2
chisla = [2, 1, 3, 4, 7, 11, 18]
kvadratd_chisla = map(kvadrat, chisla)
В результате, функция map() вернет нам ленивый итерируемый объект:
print(kvadratd_chisla)
# <map object at 0x000001F7B0AFBD90>
Давайте теперь более подробно рассмотрим, как далее должен будет работать объект kvadratd_chisla
, созданный в нашем примере функцией map():
chisla
.kvadratd_chisla
через пользовательскую функцию kvadrat() будет выполнятся определенный расчет на основе использования соответствующего элемента из исходного списка chisla
. Теперь давайте выведем на консоль то, что у нас получилось при итерации элементов map-объекта kvadratd_chisla
через функцию конструктора списка list():
print(list(kvadratd_chisla))
# [4, 1, 9, 16, 49, 121, 324]
Как мы видим, map-объект возвел в квадрат каждое число из списка chisla.
Функция map() существует во многих языках программирования, где она точна также, как и в языке Python представлена как операция преобразования. Назначение функции map() можно легко запомнить по ее названию, которое пришло из математики, и означает отображение элементов одного множества в другое, после предварительного преобразования каждого элемента* первого множества.
Итак, давайте кратко перечислим основные особенности работы с функцией map():
Рассматриваемая в этом разделе нашей статьи функция filter(), очень схожа с функцией map(), как по синтаксису, так и по функциональности. В сущности, имя данной функции filter()* говорит само за себя, так как эта функция действительно выполняет фильтрацию.
Также, как и map() функция filter() в качестве своих аргументов использует, как операцию (пользовательскую функцию) так и итерируемый объект. Важно отметить, что используемая здесь пользовательская функция должна возвращать только логические значения (true или false).
Давайте попробуем разобраться в работе вышеназванной функции с помощью соответствующего примера, в котором будем использовать все тот же список chisla и пользовательскую функцию для проверки четности nechetnost():
def nechetnost(n):
return n % 2 == 1
chisla = [2, 1, 3, 4, 7, 11, 18]
nechetnyye_chisla = filter(nechetnost, chisla)
Подобно map(), функция filter() возвращает нам ленивый итерируемый объект:
print(nechetnyye_chisla)
# <filter object at 0x0000011CE197BD90>
Теперь же давайте рассмотрим, как далее будет работать сгенерированный функцией filter() объект nechetnyye_chisla
, выполняющий фильтрацию исходного списка по нечетным числами:
nechetnyye_chisla
только нечетные числа.print(list(nechetnyye_chisla))
# [1, 3, 7, 11]
Итак, мы видим, что в результирующем объекте, сгенерированном filter() присутствуют только нечетные числа, которые были включены в filter-объект нашей пользовательской функцией nechetnost() поскольку отвечали параметру True (истинное значение) при передачи через эту функцию элементов нашего исходного списка chisla.
Примечание: Важно отметить, что функция filter() не возвращает все элементы итерируемого объекта, как это было с функцией map(), а возвращает лишь те его элементы, которые удовлетворяют условию, заданному в первом аргументе filter(), ассоциирующему в нашем случае с пользовательской функцией nechetnost()*.
Итак, давайте подытожим то, что мы уже узнали о работе функций *filter():
Изучая функции map() и *filter(), можно заметить их некоторую схожесть с другими объектами и функциями языка Python. Но, данная схожесть применительно к вышеназванным функция отнюдь не сводится к их сходству с иными объектами и функциями по названию или синтаксису. Здесь под схожестью, прежде всего, подразумевается возможность выполнения аналогичных задач иными способами, нежели только лишь названные выше функции.
Например, в предыдущем разделе нашей статьи, мы с помощью функции map() перебирали итерируемый объект и, вызывая предварительно определенную пользовательскую функцию, преобразовывали каждый элемент данного объекта.
Однако, те же самые действия можно выполнить и с помощью специальной конструкции на основе цикла – генератора-выражения. Давайте посмотрим, как это выглядит на практике:
def kvadrat(n):
return n ** 2
def map_analog(function, iterable):
return (function(x) for x in iterable)
chisla = [2, 1, 3, 4, 7, 11, 18]
kvadratd_chisla = map_analog(kvadrat, chisla)
print(list(kvadratd_chisla))
# [4, 1, 9, 16, 49, 121, 324]
Конечно же, функция map предоставляет больше различных вариантов для решения подобных задач. Но в большинстве случаев, с помощью генераторов-выражения мы получим тот же результат, что и от функции map(). Зачастую же применению генераторов-выражений следует отдавать большее предпочтение нежели map(), так как они рациональнее используют память, а код с их использованием может быть более компактным.
Аналогично выглядит ситуация и с применением функции filter. Если нам нужно перебрать итерируемый объект и поместить в новый итерируемый объект лишь те элементы, которые отвечают заданным условиям, мы можем воспользоваться как функцией filter(), так и генератором-выражения.
Давайте посмотрим, как выглядит код такого генератора – выражения на практике:
def nechetnost(n):
return n % 2 == 1
def filter_analog(function, iterable):
return (x for x in iterable if function(x))
chisla = [2, 1, 3, 4, 7, 11, 18]
nechetnyye_chisla = filter_analog(nechetnost, chisla)
print(list(nechetnyye_chisla))
# [1, 3, 7, 11]
В итоге, мы получим тот же результат, что и при использовании обычной функции filter(), поскольку генератор-выражения перебирая итерируемый объект, будет вызывать функцию nechetnost() для каждого элемента chisla в той части выражения-генератора, где указано условие. Таким образом, наш генератор-выражения будет определять элементы, которые будут включены в новый «ленивый» итерируемый объект.
В этом разделе нашей статьи мы с вами рассмотрим еще одну ситуацию, в которой применение функций map() и filter() является менее предпочтительным, нежели использование генераторов-выражений.
Допустим, нам нужно разработать программку, осуществляющую возведение в квадрат всех нечетных элементов какого-либо, задаваемого нами произвольного списка. Естественно, все нечетные значения из этого произвольно заданного списка, нам предварительно нужно еще и определить.
С помощью функций map() и filter(), эта задача решается с учетом того, что мы предварительно задаем нашей программе соответствующий исходный список chisla
:
chisla = [2, 1, 3, 4, 7, 11, 18]
Затем, применяя функции map() и filter(), мы используем значения из вышеназванного списка chisla
с тем, чтобы возвести в квадрат лишь нечетные значения данного списка (отфильтровать четные, и включить в новый итерируемый объект лишь все нечетные числа).
Для этого мы сначала используем пользовательскую функцию nechetnost() и список chisla в качестве аргументов функции filter(), а затем полученный «ленивый» итерационный объект с нечетными числами, передаем функции map() для обработки второй пользовательской функцией kvadrat()*.
В этом случае, наш код будет иметь следующий вид:
chisla = [2, 1, 3, 4, 7, 11, 18]
nechetnyy_kvadrat = map(kvadrat, filter(nechetnost, chisla))
print(nechetnyy_kvadrat)
# <map object at 0x0000020A4582B1F0>
В результате получился опять же «ленивый» итерируемый объект, содержащий квадраты нечетных чисел из нашего исходного списка chisla. Теперь, для уверенности, давайте убедимся, что полученный нами map-объект nechetnyy_kvadrat содержит именно то, что мы и предполагаем:
nechetnyy_kvadrat = map(kvadrat, filter(nechetnost, chisla))
print(list(nechetnyy_kvadrat))
# [1, 9, 49, 121]
Сейчас же, давайте попробуем решить эту задачку с применением генератора-выражения:
nechetnyy_kvadrat = (kvadrat(n) for n in chisla if nechetnost(n))
print(nechetnyy_kvadrat)
# <generator object <genexpr> at 0x000001DE9BFF1EE0>
В случае, если нам необходимо получить результат в виде списка, а не в виде сгенерированного «ленивого» итерируемого объекта, то достаточно просто оформить этот же код в виде генератора списка, заменив в нем только лишь внешние круглые скобки генератора выражения на квадратные скобки генератора списков:
nechetnyy_kvadrat = [kvadrat(n) for n in chisla if nechetnost(n)]
print(nechetnyy_kvadrat)
# [1, 9, 49, 121]
Таким образом, выполняя одну и ту же задачу различными инструментами языка Python, мы воочию убедились в том, что код с применением генераторов-выражений является несомненно более читабельным и простым для понимания, нежели код с использованием функций map() и filter():
print(list(map(kvadrat, filter(nechetnost, chisla))))
# [1, 9, 49, 121]
Неправда ли, вышеприведенный код представляет собой довольно сложную для визуального и логического восприятия, вложенную конструкцию, основанную на вызове ряда перемешанных между пользовательских и вложенных функций, каждая из которых содержит еще и ряд дополнительных аргументов.
А теперь, сравните, приведенный код с версией кода, сформированного на основе генератора-выражения:
print([kvadrat(n) for n in chisla if nechetnost(n)])
# [1, 9, 49, 121]
Здесь построения кода подобно английскому предложению, которое до неприличия просто описывает выполняемую операцию, как квадрат переменной n
, рассчитываемый для цикла, формирующего список исходя из того, что переменная n
должна быть нечетной.
Таким образом, генераторы-выражений в равнении с встроенными Python функциями map() и filter() позволяют существенно упростить код и отказаться от вызова дополнительных функций.
Мы можем еще более упростить наш код, оформив его в виде простого цикла и поместив нужные нам операции, ранее записанные в виде пользовательских функций, внутрь квадратных скобок генератора в первую и последнюю его части:
print([n**2 for n in chisla if n % 2 == 1])
# [1, 9, 49, 121]
Тут, для аналогии, можно сопоставить первую часть генератора с выводом функции map(), а последнюю – с фильтрацией за счет функции filter(). Все эти внутренние Python функции хотя и позволяют достичь нам той же цели, что и вышеназванный генератор, но в данном случае они являются излишними.
В этой статье мы с вами узнали о предназначении функций map() и filter(), а также рассмотрели основные особенности их использования для решения тех или иных задач. Теперь же, давайте еще раз повторим все подчерпнутые из этой статьи основные моменты касательно данных функций, которые нам неплохо было бы запомнить:
Мы вчера запустили новый www.pylot.me. Должны были в следующую среду, но запустили вчера.
Для улучшения качества знаний и повышения уровня программиста, необходим постоянный практикум. Где можно это организовать самостоятельно, и как практиковаться в Python?
Шпаргалка по условным конструкциям и сопоставлению структурных шаблонов