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-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.