Функциональное программирование в Python: partial и reduce

Python в первую очередь объектно-ориентированный язык. Однако он поддерживает также некоторые возможности функционального программирования, в т.ч. функции высшего порядка, благодаря которым существуют декораторы. Сегодня мы затронем две функции из встроенный библиотеки functools: reduce и partial.

Свертка или редукция reduce из functools

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

Например, функция сложения применительно к списку будет выполнять что-то вроде этого:

[1, 2, 3, 4, 5]
[3, 3, 4, 5]
[6, 4, 5]
[10, 5]
[15]

Есть разделение на правую и левую редукции. Выше продемонстрирована левая, поскольку выполняется слева направо. Именно левая редукция находится в библиотеке functools под именем reduce. Функция принимает в качестве параметров: последовательность, применяемую функцию, (опционально) начальное значение.

Вот так можно выстроить вложенные списки (здесь и далее для краткости мы будем обращаться к functools в виде ftl):

>>> import functools as ftl
>>> l = [1, 2, 3, 4, 5]
>>> ftl.reduce(lambda x, y: [x, y], l)
[[[[1, 2], 3], 4], 5]

Если бы это была правая редукция, то мы бы получили корректный список Лиспа. Или функция умножения применительно к тому же списку с учетом начального значения в виде 10:

>>> ftl.reduce(lambda x, y: x * y, l, 10)
1200

Таким образом, мы получили удесятеренный факториал.

Функция с заданными параметрами через partial из functools

Функция partial принимает в себя функцию с заданными параметрами и возвращает ее. Возвращенную функцию можно вызвать с параметрами, которые не были заданы.

Например, ниже функция пользовательская foo имеет 3 параметра. Тогда с помощью partial мы можем получить такую функцию bar, которое имеет первым параметром значение, равное 10. А к функции bar уже задавать свои значения параметров b и с.

>>> def foo(a, b, c=3):
...     return a + b - c
...
>>> bar = ftl.partial(foo, 10)
>>> bar(5, 1) # 10 + 5 - 1
14
>>> bar(5) # 10 + 5 - 3
12

Так, например, мы можем задать собственные забавные “прибавлялки” в виде функций, используя более общую функцию сложения. Вот так выглядит функция, которая всегда прибавляет 7 к своим аргументам:

>>> def add(*args):
...     return ftl.reduce(lambda x, y: x + y, args)
...
>>> add7 = ftl.partial(add, 7)
>>> add7(10)
17
>>> add7(10, 20, 30)
67

Мы даже в функции add использовали знакомую нам редукцию. Хотя могли бы написать что-то вроде такого:

>>> def add(*args):
...     res = 0
...     for i in args:
...         res += i
...     return res

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

Библиотека functools также предоставляет функцию partialmethod, которая предназначается для методов класса [1].

Подготовка данных для Data Mining на Python

Код курса
DPREP
Ближайшая дата курса
по запросу
Продолжительность
32 ак.часов
Стоимость обучения
72 000 руб.

Еще больше подробностей о функциональном программировании на Python вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и IT-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве:

Источники
  1. docs

Добавить комментарий

Поиск по сайту