PythonMiddleCoding

Объясните разницу между map(), filter() и reduce().

map() применяет функцию к каждому элементу, filter() отбирает по условию, reduce() сворачивает в одно значение. В Python 3 map и filter возвращают ленивые итераторы; в современном коде их часто заменяют list comprehension.

map(), filter(), reduce(): назначение и сигнатуры

  • map(func, iterable): применяет func к каждому элементу, возвращает ленивый итератор.
  • filter(func, iterable): оставляет элементы, для которых func возвращает True, возвращает ленивый итератор.
  • reduce(func, iterable[, initial]): сворачивает последовательность в одно значение; в Python 3 находится в functools.
from functools import reduce

numbers = [1, 2, 3, 4, 5, 6]

# map: удвоить каждый элемент
doubled = list(map(lambda x: x * 2, numbers))
print(doubled)   # [2, 4, 6, 8, 10, 12]

# filter: оставить чётные
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)     # [2, 4, 6]

# reduce: произведение всех элементов
product = reduce(lambda acc, x: acc * x, numbers)
print(product)   # 720

# reduce с initial value
total = reduce(lambda acc, x: acc + x, numbers, 0)
print(total)     # 21

map и filter возвращают итераторы (Python 3)

result = map(str, [1, 2, 3])
print(type(result))    # <class 'map'>
print(result)          # <map object at 0x...>
print(list(result))    # ['1', '2', '3']

# Итератор одноразовый
print(list(result))    # []  — уже исчерпан

Идиоматичная альтернатива в Python

В современном Python map/filter часто заменяют list comprehension — они читаются лучше:

# map → list comprehension
doubled_lc = [x * 2 for x in numbers]

# filter → list comprehension с условием
evens_lc = [x for x in numbers if x % 2 == 0]

# Комбинация map + filter
result_lc = [x * 2 for x in numbers if x % 2 == 0]

# vs map(filter(...))
result_mf = list(map(lambda x: x * 2, filter(lambda x: x % 2 == 0, numbers)))
# Читаемость хуже

Когда map/filter полезны

# 1. Применение уже существующей функции (не lambda)
strings = ["  hello  ", " world ", "  python"]
stripped = list(map(str.strip, strings))   # Лучше, чем [s.strip() for s in strings]

# 2. Работа с несколькими итерируемыми (map поддерживает несколько итерируемых)
a = [1, 2, 3]
b = [10, 20, 30]
sums = list(map(lambda x, y: x + y, a, b))   # [11, 22, 33]
# Или: list(map(sum, zip(a, b)))

# 3. Цепочка трансформаций в функциональном стиле
import operator
from functools import reduce

factorial = reduce(operator.mul, range(1, 8))   # 7! = 5040

# 4. reduce для нестандартных агрегаций
from typing import TypeVar
T = TypeVar('T')

def deep_merge(dicts: list[dict]) -> dict:
    return reduce(lambda a, b: {**a, **b}, dicts, {})

result = deep_merge([{"a": 1}, {"b": 2}, {"c": 3}])
print(result)   # {'a': 1, 'b': 2, 'c': 3}

reduce: подводные камни с пустым итерируемым

from functools import reduce

# Пустой iterable без initial — ValueError
try:
    reduce(lambda a, b: a + b, [])
except ValueError as e:
    print(e)   # reduce() of empty iterable with no initial value

# С initial — безопасно
result = reduce(lambda a, b: a + b, [], 0)   # 0

Подводные камни

  • map/filter — итераторы, не списки: передача в len() вызывает TypeError; нужно материализовать через list() или tuple().
  • reduce на пустом iterable: без initial вызывает ValueError.
  • lambda в map читается хуже: если трансформация нетривиальна — лучше именованная функция или comprehension.
  • reduce для сложных агрегаций: трудно отлаживать, трудно читать — часто лучше явный цикл.
  • map с несколькими итерируемыми разной длины: итерация прекращается по самому короткому — как zip(), не zip_longest().
  • Изменение типов в filter: filter(None, items) фильтрует falsy значения (0, "", [], None) — не только None.

Common mistakes

  • Описывать map filter reduce только как термин и не показывать механизм на минимальном примере.
  • Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
  • Не связывать поведение с официальным контрактом Python и реальной эксплуатацией.

What the interviewer is testing

  • Объясняет map filter reduce через последовательность действий, а не через набор ключевых слов.
  • Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
  • Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.

Sources

Related topics