Functools.reduce: примеры (PYTHON)
functools.reduce(function, iterable, initializer): objectОсновы функции functools.reduce
Функция reduce() из модуля functools применяет переданную функцию function кумулятивно к элементам итерируемого объекта iterable, сводя его к единственному значению. Её основное применение связано с операциями накопления результата.
Функция используется, когда требуется последовательно обработать элементы коллекции и получить агрегированный результат, такой как сумма, произведение, поиск максимального значения или объединение структур данных.
Аргументы функции:
- function - функция двух аргументов. Первый аргумент представляет текущее накопленное значение, второй - следующий элемент из iterable.
- iterable - итерируемый объект, элементы которого подлежат обработке.
- initializer (опциональный) - начальное значение для аккумулятора. Если значение указано, вычисления начинаются с него. При отсутствии initializer функция использует первый элемент iterable в качестве начального значения, и обработка начинается со второго элемента.
Возвращаемое значение - финальное накопленное значение. При пустом iterable и отсутствии initializer вызывается исключение TypeError. Если iterable пуст, но задан initializer, возвращается значение initializer.
Примеры использования functools.reduce
Вычисление суммы чисел:
from functools import reduce
result = reduce(lambda acc, x: acc + x, [1, 2, 3, 4])
print(result)10
Вычисление произведения с начальным значением:
from functools import reduce
result = reduce(lambda acc, x: acc * x, [1, 2, 3, 4], 10)
print(result)240
Поиск максимального элемента:
from functools import reduce
result = reduce(lambda a, b: a if a > b else b, [5, 1, 8, 3])
print(result)8
Объединение списка строк:
from functools import reduce
result = reduce(lambda acc, s: acc + '-' + s, ['a', 'b', 'c'], 'start')
print(result)start-a-b-c
Альтернативные функции в Python
В стандартной библиотеке Python существуют функции для частных случаев свертки:
- sum(iterable, start=0) - специализированная функция для сложения чисел. Предпочтительнее reduce для суммирования из-за лучшей читаемости и производительности.
- math.prod(iterable, start=1) - вычисляет произведение элементов. Появилась в Python 3.8 как специализированная альтернатива.
- any(iterable) и all(iterable) - проверяют логические условия. Эффективнее и понятнее соответствующих конструкций с reduce.
- itertools.accumulate(iterable, func=operator.add) - возвращает итератор по промежуточным результатам свертки, а не только по конечному значению.
Выбор между reduce и специализированной функцией зависит от ясности кода. Встроенные функции sum и prod являются предпочтительными для арифметических операций.
Аналоги функции в других языках
Идея операции свертки присутствует во многих языках программирования.
JavaScript (Array.prototype.reduce):
const result = [1, 2, 3, 4].reduce((acc, val) => acc + val, 0);
console.log(result);10
PHP (array_reduce):
$result = array_reduce([1, 2, 3, 4], function($carry, $item) {
return $carry + $item;
}, 0);
echo $result;10
Kotlin (fold и reduce):
val list = listOf(1, 2, 3, 4)
val foldResult = list.fold(0) { acc, i -> acc + i }
val reduceResult = list.reduce { acc, i -> acc + i }
println("fold: $foldResult, reduce: $reduceResult")fold: 10, reduce: 10
Отличия часто заключаются в наличии двух вариантов: с начальным значением (fold) и без него (reduce), а также в порядке аргументов передаваемой функции. В Python есть только одна функция reduce, которая через аргумент initializer реализует оба сценария.
Распространенные ошибки
Передача пустой последовательности без начального значения:
from functools import reduce
try:
result = reduce(lambda x, y: x + y, [])
except TypeError as e:
print(f"Ошибка: {e}")Ошибка: reduce() of empty iterable with no initial value
Использование функции с неправильным числом аргументов:
from functools import reduce
try:
# Функция ожидает два аргумента, а не один
result = reduce(lambda x: x * 2, [1, 2, 3])
except TypeError as e:
print(f"Ошибка: {e}")Ошибка: <lambda>() takes 1 positional argument but 2 were given
Непреднамеренное изменение типа данных при работе с разными типами:
from functools import reduce
# Начальное значение 0 (int) приводит к преобразованию строки в операторе +
result = reduce(lambda acc, s: acc + len(s), ["a", "bb"], 0)
print(result)
# Но если забыть initializer, первая операция будет 'a' + len('bb'), что вызовет ошибку.
try:
result = reduce(lambda acc, s: acc + len(s), ["a", "bb"])
except TypeError as e:
print(f"Другая ошибка: {e}")3 Другая ошибка: can only concatenate str (not "int") to str
Изменения в последних версиях
В Python 3.x функция functools.reduce не претерпела значительных изменений в своей основе. Первоначально она была встроенной функцией reduce в Python 2.x. В Python 3.0 её переместили в модуль functools в рамках общего стремления к тому, чтобы явно импортировать функции высшего порядка.
Интерфейс и поведение функции остаются стабильными. Все улучшения касаются в основном производительности и внутренней реализации, незаметной для пользователя.
Расширенные примеры применения
Построение вложенного словаря из последовательности пар ключ-значение:
from functools import reduce
data = [('a', 1), ('b', 2), ('c', 3)]
result = reduce(lambda d, item: {**d, item[0]: item[1]}, data, {})
print(result){'a': 1, 'b': 2, 'c': 3}Композиция нескольких функций:
from functools import reduce
def compose(*funcs):
return reduce(lambda f, g: lambda x: f(g(x)), funcs, lambda x: x)
def add_two(x):
return x + 2
def mul_three(x):
return x * 3
composed = compose(add_two, mul_three, add_two)
print(composed(5)) # ((5 + 2) * 3) + 223
Поиск самого длинного слова в списке:
from functools import reduce
words = ['Python', 'reduce', 'function', 'example']
longest = reduce(lambda a, b: a if len(a) > len(b) else b, words)
print(longest)function
Суммирование только четных чисел с накоплением промежуточных результатов:
from functools import reduce
steps = []
def sum_even(acc, x):
if x % 2 == 0:
steps.append(f"Add {x} to {acc}")
return acc + x
steps.append(f"Skip {x}")
return acc
result = reduce(sum_even, [1, 2, 4, 3, 6], 0)
print("Result:", result)
print("Steps:", ' -> '.join(steps))Result: 12 Steps: Skip 1 -> Add 2 to 0 -> Add 4 to 2 -> Skip 3 -> Add 6 to 6
Рекурсивное объединение списков из вложенной структуры:
from functools import reduce
nested = [[1, 2], [3, [4, 5]], [6]]
flatten = reduce(lambda acc, val: acc + (flatten(val) if isinstance(val, list) else [val]), nested, [])
def flatten(lst):
return reduce(lambda acc, val: acc + (flatten(val) if isinstance(val, list) else [val]), lst, [])
print(flatten(nested))[1, 2, 3, 4, 5, 6]