Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs
Следующая весьма
полезная функция reduce модуля functools позволяет
последовательно обрабатывать элементы итерируемых объектов, таких как, списки,
кортежи, строки, словари, множества и т.п. Давайте изучим порядок ее работы на
конкретных примерах.
Предположим, нам
необходимо просуммировать значения следующего списка:
Конечно, мы
легко можем это сделать с помощью функции sum() языка Python:
Но мы реализуем
эту логику с помощью функции reduce(). Эта функция первым аргументом
ожидаем ссылку на функцию двух параметров и возвращающую одно вычисленное
значение. Второй обязательный аргумент у reduce() – это
итерируемый объект. Вначале объявим функцию для сложения элементов списка:
def reduce_sum(a, b):
return a + b
И вызовем
непосредственно функцию reduce() следующим образом:
res = functools.reduce(reduce_sum, data)
В переменной res будет храниться
значение 15 – сумма всех элементов списка data. Как получился
такой результат? Давайте разберемся.
Вначале функция reduce() вызывает
переданную ей функцию reduce_sum с аргументами a=7, b=2, то есть,
выбираются первые два значения из списка data. Функция reduce_sum() суммирует эти
величины, и результат сохраняется в некоторой переменной, пусть это будет
переменная r. На следующей
итерации берется следующее значение b=5, а в параметр ‘a’ передается
ранее вычисленное значение r. В результате, к сумме двух элементов
добавляется следующее значение 5. Получаем результат сложения трех чисел r=14. Этот
процесс вычислений продолжается, пока не будет достигнут конец списка data. Итоговый
результат r=15 возвращается,
как результат работы функции reduce().
Конечно, когда
передаваемая функция reduce_sum примитивна
(суммирует два числа), вместо нее удобнее использовать лямбда-функцию:
res = functools.reduce(lambda a, b: a + b, data)
Результат будет
абсолютно тем же самым. Если же вместо сложения прописать операцию умножения
двух величин:
res = functools.reduce(lambda a, b: a * b, data)
то будет
вычисляться произведение элементов списка data. Или можно
реализовать логику поиска наибольшего значения:
res = functools.reduce(lambda a, b: a if a > b else b, data)
По аналогии
можно прописывать и более сложные вычисления, для которых нет аналогов в
стандартных функциях языка Python. Например, сумму модулей:
res = functools.reduce(lambda a, b: abs(a) + abs(b), data)
Или объединение
строк с их предобработкой:
cities = ["уфа ", " казань ", "омск", " ярославль"]
res = functools.reduce(lambda a, b: a.strip().title() + ";" + b. strip().title(), cities)
И так далее. Везде,
где происходит накопление данных, при обработке последовательностей, можно
попробовать реализовать это с помощью функции reduce().
А как будет себя
вести эта функция, если в списке только один элемент? Давайте посмотрим, выполним
программу:
def reduce_sum(a, b):
print(f"{a, b}")
return a + b
res = functools.reduce(reduce_sum, [1])
Видим, что
функция reduce_sum ни разу не была
вызвана. Следовательно, при одном единственно элементе, он просто возвращается.
Но, если следующим аргументом указать некоторое начальное значение суммы,
например:
res = functools.reduce(reduce_sum, [1], 10)
то функция reduce_sum будет вызвана,
увидим строчку:
(10, 1)
то есть параметр
a=10, b=1 и итоговая
сумма равна 11. Таким образом, переданная функция вызывается только в том
случае, если имеются, как минимум два значения.
Если же
вызывается reduce() для пустой
последовательности:
res = functools.reduce(reduce_sum, [])
то генерируется
исключение TypeError. Но, при указании начального значения:
res = functools.reduce(reduce_sum, [], 2)
оно будет
возвращаться функцией reduce() без вызова функции reduce_sum, т.к. здесь нет
второго значения для параметра b.
Вот такие
особенности работы функции reduce() следует иметь в виду при ее
использовании в своих программах. Самое главное правильно формировать
передаваемую функцию. В качестве последнего примера приведу использование
функции reduce() для
определения наличия хотя бы одного четного значения в списке data:
data = [7, 21, 5, -3, 3]
res = functools.reduce(lambda a, b: a or b % 2 == 0, data, False)
Разберитесь
детально в порядке ее работы и в качестве домашнего задания реализуйте по
аналогии определения наличия всех четных элементов в списке data.
Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs