Фильтрация списков в Python — функция filter

Помимо сортировки частым действием по отношению к спискам (или другим составным объектам) является их фильтрация. Например, надо убрать из списка отрицательные значения, или пустые строки, или оставить в нем только объекты с определенными свойствами.

В Python для решения подобных задач достаточно написать небольшой код. Пусть из смешанного списка требуется отфильтровать только числа. Код без "синтаксического сахара" будет выглядеть примерно так:

a = ['one', 5, 'two', 4.22, 8, 'j', 0.35]
b = []
for item in a:
    if type(item) in (int, float):
        b.append(item)
print(b)

Результат:

[5, 4.22, 8, 0.35]

Здесь фильтрация выполняется в цикле for. Поскольку цикл достаточно прост, мы можем заменить его списочным выражением:

a = ['one', 5, 'two', 4.22, 8, 'j', 0.35]
b = [item for item in a if type(item) in (int, float)]
print(b)

Однако это не всё. В Python есть функция filter, которая непосредственно предназначена для решения задач фильтрации. Он принимает два аргумента: первый – функцию, второй – итерируемый объект (последовательность — строки, списки и кортежи, итератор или объект, поддерживающий итерацию). В этом уроке вторым аргументом мы будем передавать только списки.

Функция, которая передается первым аргументом в filter, вызывается на каждый элемент списка. Если эта функция возвращает что угодно, но не объекты None, False и их эквиваленты (0, [], '' и т. п.), то переданный ей текущий элемент списка проходит фильтрацию, он остается.

В filter можно передавать как обычную, так и лямбда-функцию. Рассмотрим сначала пример с обычной функцией:

def numbers(item):
    if type(item) in (int, float):
        return True
    else:
        return False


a = ['one', 5, 'two', 4.22, 8, 'j', 0.35]
b = filter(numbers, a)
print(list(b))

Обратим внимание, функция filter возвращает не список, а итератор. Для простоты вывода на экран мы преобразовываем его в список.

В функции numbers мы прописываем ветку else для ясности кода. Однако ее можно опустить.

def numbers(item):
    if type(item) in (int, float):
        return True

Фильтрация и в этом случае будет работать также. Почему? Когда в numbers передается не число, выражение return True не выполняется. Получается, что функция ничего не возвращает. На самом деле в таких случаях функция возвращает объект None. В свою очередь для функции filter что None, что False – это сигнал отбросить элемент.

Заменим функцию numbers lambda-выражением:

a = ['one', 5, 'two', 4.22, 8, 'j', 0.35]
b = filter(lambda item: type(item) in (int, float), a)
print(list(b))

Выражение type(item) in (int, float) логическое, оно возвращает либо True, либо False.

Функция filter в качестве первого аргумента может принимать не только другую функцию, но и объект None. В этом случае элементы списка будут оцениваться сами по себе как True или False (или их эквиваленты). В примере ниже из первого списка будут изъяты нули, из второго – пустые строки.

a = [8, -1, 0, 3, 0]
b = ['a', 'b', '', 'dd']
f_a = filter(None, a)
f_b = filter(None, b)
print(list(f_a))
print(list(f_b))

Результат:

[8, -1, 3]
['a', 'b', 'dd']

Практическая работа

Пользователь вводит два натуральных числа, первое больше второго. Используя функцию filter, вывести на экран все натуральные числа, кратные второму числу и не превышающие первое. Например, были введены 10 и 3. Вывод: 3, 6, 9.