Что делает функция filter в Python
Что проверяют
Понимаете ли вы ленивые итераторы и выбираете ли между filter и генераторным выражением осознанно. Вопрос простой на вид, но по нему видно, писали вы код на Python 3 или помните поведение второй версии.
Как отвечать
filter(func, iterable) возвращает итератор, который отдаёт только те элементы, на которых func вернула истинное значение. Ничего не вычисляется, пока результат не начнут перебирать, исходная последовательность не меняется.
nums = [1, 2, 3, 4, 5, 6]
evens = filter(lambda n: n % 2 == 0, nums)
print(list(evens)) # [2, 4, 6]Отдельно стоит знать про None в первом аргументе: filter(None, items) выбрасывает всё ложное, то есть нули, пустые строки, пустые списки и None.
Что выбирать на практике:
- генераторное выражение читается лучше и обычно работает быстрее, чем
filterсlambda: на каждом элементе не возникает лишнего вызова питоновской функции; filterвыигрывает, когда предикат уже готовая функция, особенно реализованная на C:filter(str.isdigit, chars),filter(None, rows);- если результат нужен списком сразу, берут списковое включение, а не обёртку
list(filter(...)).
Полезно добавить, что лень оправдана на больших и потенциально бесконечных источниках: память не растёт, обработка идёт потоком, и это тот же принцип, что в itertools.
Где спотыкаются
Перебирают результат дважды. Итератор одноразовый, второй проход вернёт пустоту, и ошибка всплывает в коде, где сначала посчитали длину, а потом попытались пройти заново.
Второе: ждут список, как во второй версии языка. Срез, индекс и len() к объекту filter неприменимы, и это выясняется уже в рантайме.
Третье: кладут в предикат обращение к базе или к файлу. Из-за лени вызовы происходят не там, где написаны, а в момент перебора, иногда уже за пределами блока with, на закрытом соединении.