Можно ли использовать map вместо filter для подсчета количества элементов в последовательности в Python
Что проверяют
Знаете ли вы, что map и filter в Python 3 возвращают ленивые итераторы, и умеете ли посчитать элементы без лишних промежуточных списков. Ключевой момент: bool в Python наследуется от int, поэтому True складывается как единица. Следом обычно спрашивают, что быстрее и что читается лучше.
Как отвечать
Можно, и иногда это даже удобнее. Предикат возвращает True или False, а sum() складывает их как 1 и 0:
sum(map(lambda x: x % 2 == 0, numbers))
Для сравнения варианты подсчёта с их ценой:
sum(1 for x in numbers if x % 2 == 0): самый читаемый и идиоматичный, память O(1), генератор не строит список;sum(map(pred, numbers)): то же по памяти, особенно хорош со встроенной функцией без лямбды, напримерsum(map(str.isdigit, text));len(list(filter(pred, numbers))): работает, но строит список всех подходящих элементов только ради его длины. На миллионе элементов это лишние мегабайты;numbers.count(value)иcollections.Counter: когда нужно посчитать конкретное значение или частоты всех значений сразу;- NumPy или pandas:
np.count_nonzero(arr % 2 == 0)или(series % 2 == 0).sum()на больших числовых массивах в разы быстрее любого цикла на чистом Python.
По скорости: map и filter с lambda платят за вызов функции на каждом элементе и обычно не быстрее генераторного выражения. Выигрыш бывает, когда передаёте встроенную функцию, реализованную на C. Если разница важна для задачи, измерьте её через timeit, а не на глаз.
Где спотыкаются
Пишут len(filter(...)). В Python 3 это итератор без длины, и вызов бросит TypeError.
Используют sum(map(...)) с функцией, которая возвращает не bool, а произвольное значение, например строку или остаток от деления. Сумма либо упадёт, либо посчитает не то. Оборачивайте результат в bool() или переходите на генератор с if.
Переиспользуют итератор. Объект map или filter проходится один раз: после подсчёта через sum() он пуст, и повторный перебор молча вернёт ноль элементов.