Какой предикат используется для фильтрации коллекции в Python
Что проверяют
Знаете ли вы, что предикат в Python это просто вызываемый объект, который для элемента возвращает истинное или ложное значение, и какими средствами языка он применяется к коллекции. Следом обычно спрашивают, что лучше: filter() или генератор списка, и почему filter() в Python 3 возвращает не список.
Как отвечать
Отдельного типа «предикат» в языке нет. Им может быть обычная функция, lambda, метод вроде str.isdigit или любой объект с __call__. Результат не обязан быть именно bool: Python проверяет истинность, поэтому 0, пустая строка и None считаются ложью.
Инструменты, которые принимают предикат:
filter(pred, iterable): возвращает ленивый итератор, элементы вычисляются по мере обхода. Чтобы получить список, оборачивают вlist();filter(None, iterable): особый случай, отбрасывает все ложные значения без отдельной функции;- генератор списка
[x for x in items if pred(x)]: самый читаемый вариант, заодно позволяет сразу преобразовать элемент; itertools.filterfalse: обратный фильтр, оставляет элементы, для которых предикат ложен;itertools.takewhileиdropwhile: берут или пропускают элементы, пока условие выполняется, и останавливаются на первом несовпадении;any()иall(): не фильтруют, а отвечают, есть ли хоть один подходящий элемент и все ли подходят, и прекращают обход на первом решающем.
По скорости filter с lambda и генератор списка близки, с небольшим перевесом генератора: нет вызова функции на каждом элементе. filter выигрывает, когда предикат уже встроенная функция, например filter(str.isalpha, words).
Где спотыкаются
Забывают, что filter() возвращает итератор. Пройти его можно один раз, второй list() от того же объекта даст пустой список.
Ждут от filter преобразования элементов. Предикат только решает, оставить элемент или нет, а сам элемент попадает в результат без изменений. Нужна обработка, это map() или выражение в генераторе.
Фильтруют список, удаляя из него элементы через remove() прямо в цикле. Индексы сдвигаются, и часть элементов пропускается. Правильно собрать новый список или присвоить срез items[:] = [...].