Share This
Связаться со мной
Крути в низ
Categories
//Комбинирование map() с другими инструментами в Python

Комбинирование map() с другими инструментами в Python

16.04.2021Category : Python

Третья (заключительная) часть статьи «Python’s map(): Processing Iterables Without a Loop», перевод которой выполнил и опубликовал сайт webdevblog.ru. В первой части мы познакомились с map(), во второй рассмотрели тему преобразования итераций строк и чисел. Здесь рассмотрим следующие темы:

  • Комбинирование map() с другими функциональными инструментами
    • map() и filter()
    • map() и reduce()
  • Обработка итераций на основе кортежей с помощью starmap()
  • Кодирование в питоническом стиле: замена map()
    • Использование list comprehension
    • Использование выражений генератора

Комбинирование map() с другими функциональными инструментами

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

map() и filter()

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

  1. function будет предикатом или функцией с логическим значением, функцией, которая возвращает True или False в соответствии с входными данными.
  2. iterable будет любым итерабельным объектом Python.

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

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

>>> import math  >>> math.sqrt(-16) Traceback (most recent call last):   File "<input>", line 1, in <module>     math.sqrt(-16) ValueError: math domain error

Если в качестве аргумента указано отрицательное число, math.sqrt() вызывает ошибку ValueError. Чтобы избежать этой проблемы, вы можете использовать filter(), чтобы отфильтровать все отрицательные значения, а затем найти квадратный корень из оставшихся положительных значений. Посмотрите на следующий пример:

>>> import math  >>> def is_positive(num): ...     return num >= 0 ...  >>> def sanitized_sqrt(numbers): ...     cleaned_iter = map(math.sqrt, filter(is_positive, numbers)) ...     return list(cleaned_iter) ...  >>> sanitized_sqrt([25, 9, 81, -16, 0]) [5.0, 3.0, 9.0, 0.0]

is_positive() — это функция-предикат, которая принимает число в качестве аргумента и возвращает True, если число больше или равно нулю. Вы можете передать is_positive() в filter(), чтобы удалить все отрицательные числа. Таким образом, вызов map() будет обрабатывать только положительные числа, а math.sqrt() не выдаст вам ValueError.

map() и reduce()

Функция reduce() находится в модуле под названием functools в стандартной библиотеке Python. reduce() — еще один основной функциональный инструмент в Python, который полезен, когда вам нужно применить функцию к итерируемому объекту и уменьшить его до одного накопительного значения. Этот вид операции обычно известен как сокращение или складывание. reduce() принимает два обязательных аргумента:

  1. function может быть любым вызываемым объектом Python, который принимает два аргумента и возвращает значение.
  2. iterable может быть любым итерируемым объектом Python.

reduce() применяет функцию ко всем элементам в итерации и кумулятивно вычисляет окончательное значение.

Вот пример, который объединяет map() и reduce() для вычисления общего размера всех файлов, которые находятся в вашем домашнем каталоге, в совокупности:

>>> import functools >>> import operator >>> import os >>> import os.path  >>> files = os.listdir(os.path.expanduser("~"))  >>> functools.reduce(operator.add, map(os.path.getsize, files)) 4377381

В этом примере вызывается os.path.expanduser(«~»), чтобы получить путь к вашему домашнему каталогу. Затем вызывается os.listdir() по этому пути, чтобы получить список с путями всех файлов, которые там находятся.

Вызов map() использует os.path.getsize() для получения размера каждого файла. Наконец, используется reduce() с operator.add(), чтобы получить совокупную сумму размера каждого отдельного файла. Конечный результат — это общий размер всех файлов в вашем домашнем каталоге в байтах.

Примечание. Несколько лет назад Google разработал и начал использовать модель программирования, которую они назвали MapReduce. Это был новый стиль обработки данных, предназначенный для управления большими данными с использованием параллельных и распределенных вычислений в кластере.

Эта модель была вдохновлена комбинацией операций map и reduce, обычно используемых в функциональном программировании.

Модель MapReduce оказала огромное влияние на способность Google обрабатывать огромные объемы данных в разумные сроки. Однако к 2014 году Google больше не использовал MapReduce в качестве основной модели обработки.

В настоящее время вы можете найти несколько альтернативных реализаций MapReduce, таких как Apache Hadoop, который представляет собой набор программных утилит с открытым исходным кодом, использующих модель MapReduce.

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

>>> import os >>> import os.path  >>> files = os.listdir(os.path.expanduser("~"))  >>> sum(map(os.path.getsize, files)) 4377381

Этот пример намного удобнее и эффективнее, чем предыдущий. Если вы хотите глубже понять, как использовать reduce() и какие альтернативные инструменты вы можете использовать для замены reduce() питоническим способом, то ознакомьтесь с Python’s reduce(): From Functional to Pythonic Style.

Обработка итераций на основе кортежей с помощью starmap()

Функция itertools.starmap() в Python создает итератор, который применяет функцию к аргументам, полученным из итерации кортежей, и возвращает результаты. Это полезно, когда вы обрабатываете итерации, которые уже сгруппированы в кортежи.

Основное различие между map() и starmap() заключается в том, что последний вызывает свою функцию преобразования с помощью оператора unpack() для распаковки каждого кортежа аргументов в несколько позиционных аргументов. Итак, функция преобразования вызывается как функция ( args) вместо функции (arg1, arg2, … argN).

В официальной документации для starmap() говорится, что эта функция примерно эквивалентна следующей функции Python:

def starmap(function, iterable):     for args in iterable:         yield function(*args)

Цикл for в этой функции выполняет итерацию по элементам в итерации и в результате выдает преобразованные элементы. Вызов function(* args) использует оператор unpack  для распаковки кортежей в несколько позиционных аргументов. Вот несколько примеров того, как работает starmap():

>>> from itertools import starmap  >>> list(starmap(pow, [(2, 7), (4, 3)])) [128, 64]  >>> list(starmap(ord, [(2, 7), (4, 3)])) Traceback (most recent call last):   File "<input>", line 1, in <module>     list(starmap(ord, [(2, 7), (4, 3)])) TypeError: ord() takes exactly one argument (2 given)

В первом примере используется pow() для вычисления степени первого значения, возведенного во второе значение в каждом кортеже. Кортежи будут в форме (base, exponent).

Если каждый кортеж в вашей итерации имеет два элемента, тогда функция также должна принимать два аргумента. Если кортежи содержат три элемента, функция должна принимать три аргумента и так далее. В противном случае вы получите ошибку TypeError.

Если вы используете map() вместо starmap(), вы получите другой результат, потому что map() берет по одному элементу из каждого кортежа:

>>> list(map(pow, (2, 7), (4, 3))) [16, 343]

В этом случае у вас есть два кортежа вместо списка кортежей. Вы также поменяли местами 7 и 4. Теперь первый кортеж предоставляет основания, а второй кортеж — показатели.

Кодирование в питоническом стиле: замена map()

Инструменты функционального программирования, такие как map()filter() и reduce(), существуют уже давно. Однако списковые включения (list comprehensions) и выражения генератора (generator expressions) стали их естественной заменой почти в каждом случае использования.

Например, функциональность, предоставляемая map(), почти всегда лучше выражается с использованием list comprehensions или generator expressions. В следующих двух разделах вы узнаете, как заменить вызов map() list comprehensions или generator expressions, чтобы сделать ваш код более читабельным и питоническим.

Использование list comprehension

Существует общий шаблон, который можно использовать для замены вызова map() list comprehension. Вот как:

# Generating a list with map list(map(function, iterable))  # Generating a list with a list comprehension [function(x) for x in iterable]

Обратите внимание, что list comprehension почти всегда читается более просто, чем вызов map(). Поскольку list comprehension довольно популярны среди разработчиков Python, их можно найти повсюду. Таким образом, замена вызова map() list comprehension сделает ваш код более знакомым для других разработчиков Python.

Вот пример того, как заменить map() для построения списка чисел в квадрате:

>>> # Transformation function >>> def square(number): ...     return number ** 2  >>> numbers = [1, 2, 3, 4, 5, 6]  >>> # Using map() >>> list(map(square, numbers)) [1, 4, 9, 16, 25, 36]  >>> # Using a list comprehension >>> [square(x) for x in numbers] [1, 4, 9, 16, 25, 36]

Если вы сравните оба решения, то можете сказать, что тот, который использует list comprehension, более читабелен, потому что он читается почти как обычный английский. Кроме того, list comprehension позволяет избежать необходимости явно вызывать list() в map() для построения окончательного списка.

Использование generator expressions

map() возвращает объект, который является итератором, выдающим элементы по запросу. Итак, естественной заменой map() является generator expressions, поскольку оно возвращают объекты, которые также являются итераторами, дающими элементы по запросу.

Как известно, итераторы Python довольно эффективны с точки зрения потребления памяти. По этой причине map() теперь возвращает итератор вместо списка.

Между list comprehension и generator expressions есть крошечная синтаксическая разница. В первом используется пара квадратных скобок ([]) для ограничения выражения. Во втором используются круглые скобки (()). Итак, чтобы превратить list comprehension в generator expressions, вам просто нужно заменить квадратные скобки круглыми скобками.

Вы можете использовать generator expressions для написания кода, который читается лучше, чем код, использующий map(). Посмотрите на следующий пример:

>>> # Transformation function >>> def square(number): ...     return number ** 2  >>> numbers = [1, 2, 3, 4, 5, 6]  >>> # Using map() >>> map_obj = map(square, numbers) >>> map_obj <map object at 0x7f254d180a60>  >>> list(map_obj) [1, 4, 9, 16, 25, 36]  >>> # Using a generator expression >>> gen_exp = (square(x) for x in numbers) >>> gen_exp <generator object <genexpr> at 0x7f254e056890>  >>> list(gen_exp) [1, 4, 9, 16, 25, 36]

Этот код имеет главное отличие от кода из предыдущего раздела: вы меняете квадратные скобки на пару круглых скобок, чтобы превратить list comprehension в generator expressions.

Generator expressions обычно используются в качестве аргументов в вызовах функций. В этом случае вам не нужно использовать круглые скобки для создания generator expressions, потому что круглые скобки, которые вы используете для вызова функции, также предоставляют синтаксис для построения генератора. Используя эту идею, вы можете получить тот же результат, что и в приведенном выше примере, вызвав list() следующим образом:

>>> list(square(x) for x in numbers) [1, 4, 9, 16, 25, 36]

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

Generator expressions столь же эффективны, как map(), с точки зрения потребления памяти, потому что оба они возвращают итераторы, которые выдают элементы по запросу. Однако generator expressions почти всегда улучшают читаемость вашего кода. Они также делают ваш код более Pythonic в глазах других разработчиков Python.

Заключение

Функция map() в Python позволяет выполнять операции сопоставления (mapping) с итерациями. Операция сопоставления состоит из применения функции преобразования к элементам в итерации для создания преобразованной итерации. В общем, map() позволяет обрабатывать и преобразовывать итерации без использования операторов цикла.

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

Теперь вы знаете, как:

  • Работать с map в Python
  • Использовать map() для обработки и преобразования итераций без использования опереторов цикла
  • Объединить map() с такими функциями, как filter() и reduce(), чтобы выполнять сложные преобразования
  • Заменить map () такими инструментами, как list comprehensions и generator expressions

Обладая этими новыми знаниями, вы сможете использовать map() в своем коде придерживаясь функционального стиля программирования. Вы также можете переключиться на более питонический и современный стиль, заменив map() на list comprehension или generator expression.

kombinirovanie map s drugimi instrumentami v python 3fda924 - Комбинирование map() с другими инструментами в Python

Хотите больше материалов по Python

Подписывайтесь на нас в Телеграм

Подписаться ×

  • 6 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close