Язык программирования Python

Как фильтровать списки в Python. Функция Filter.

Подобно золотоискателю, который хочет отделить золотые самородки от грязи, в этой статье я покажу вам, как можно сохранить в списке те значения, которые вас интересуют, и отбросить те, которые не нужны. Другими словами, я покажу вам, как фильтровать список или любую коллекцию данных.

Это можно сделать с помощью метода анализа списка таким образом: [ x for x in l if f(x) ], где f – функция, которая решает с помощью булева числа, должен ли элемент списка l быть в отфильтрованном списке. Вы также можете использовать функцию фильтрации так: list(filter(f, l)).

Но, как всегда, давайте будем разбираться по порядку.

Как фильтровать список с помощью метода анализа списка

Предположим, что у нас есть список с различными целыми числами, и мы хотим отфильтровать его, чтобы в итоге получить список, в котором будут только четные числа. Это характерная задача для тех, кто начинает изучать Python или программирование в целом.

Первое, что приходит на ум в этом случае, – сделать цикл для перебора всех элементов исходного списка.

Внутри этого цикла мы проверяем, удовлетворяет ли каждый элемент условию нахождения в отфильтрованном списке, то есть проверяем, является ли число четным.

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

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

Давайте рассмотрим пример:

list = [3, 1, 4, 7, 2, 8, 9, 11, 10] # список, который мы хотим отфильтровать filtered = [] # список, содержащий отфильтрованные элементы for element in list: if not element % 2: filtered.append(element) print(filtered) # отображаем результат
Code language: PHP (php)

Этот код выведет следующий результат:

[4, 2, 8, 10]
Code language: JSON / JSON with Comments (json)

Как видите, код довольно прост и понятен, но давайте обсудим несколько моментов:

  • Нам нужно заранее создать отфильтрованный список, чтобы мы могли добавлять элементы по мере необходимости.
  • Нам нужно включить в цикл условие. В данном случае условие простое, но оно могло бы быть более сложным, что сделало бы код менее наглядным и более трудным для чтения и понимания.
  • Код относительно длинный для очень обычной операции и, в принципе, должен быть проще.

Давайте рассмотрим способы решения этих вопросов.

Если условие включения элемента в отфильтрованный список становится немного сложнее, может быть интересно заключить его в функцию. Таким образом, мы можем вызвать эту функцию из цикла, что делает сам его более простым.

Как видите, код довольно прост и понятен, но давайте обсудим несколько моментов:

Нам нужно заранее создать отфильтрованный список, чтобы мы могли добавлять элементы по мере необходимости.
Нам нужно включить в цикл условие. В данном случае условие простое, но оно могло бы быть более сложным, что сделало бы код менее наглядным и более трудным для чтения и понимания.
Код относительно длинный для очень обычной операции и, в принципе, должен быть проще.
Давайте рассмотрим способы решения этих вопросов. Если вы не совсем понимаете условное условие в предыдущем примере, где, очевидно, нет сравнения, вы можете взглянуть на эту статью об условных выражениях в Python, где я объясняю, среди прочего, почему вы можете работать с целым числом (которое является результатом элемента % 2) так, как если бы это было булево значение, то есть True или False.

Если условие включения элемента в отфильтрованный список становится немного сложнее, может быть интересно заключить его в функцию. Таким образом, мы можем вызвать эту функцию из цикла, что делает сам цикл более простым.

Для примера можно представить, что нам нужны те элементы списка, которые являются четными, или те, которые кратны 3 или кратны 11. Таким образом, условие будет выглядеть следующим образом: не элемент % 2 или не элемент % 3 или не элемент % 11.

Как вы можете себе представить, это сделает цикл не очень красивым:

list = [3, 1, 4, 7, 2, 8, 9, 11, 10] # список, который мы хотим отфильтровать filtered = [] # список, содержащий отфильтрованные элементы for element in list: if not element % 2 or not element % 3 or not element % 11: filtered.append(element) print(filtered) # выводим результат
Code language: PHP (php)

Ситуация становится еще хуже, если условие усложняется (представьте, что нам нужны простые числа в списке или что-то еще в этом роде). В таких случаях лучше всего вынести условие из цикла.

Мы можем сделать это, поместив его в функцию, тем самым получив более модульный и организованный код, который легче понять и поддерживать. Назовем эту функцию condition:

def condition(num): return not num % 2 or not num % 3 or not num % 11 list = [3, 1, 4, 7, 2, 8, 9, 11, 10] # список, который мы хотим отфильтровать filtered = [] # список, содержащий отфильтрованные элементы for element in lista: if condition(element): filtered.append(element) print(filtered) # выводим результат
Code language: PHP (php)

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

Однако можно сказать, что этот код не очень похож на Python. Воспользуемся сжатием списка и решим пункты 1 и 3 выше, т.е. нам не нужно предварительно создавать пустой список, и тем самым мы еще больше упростим код.

Сжатие списка позволяет нам формировать список “на лету”.

В этом случае пример будет выглядеть следующим образом:

def condition(num): return not num % 2 or not num % 3 or not num % 11 list = [3, 1, 4, 7, 2, 8, 9, 11, 10] filtered = [ element for element in list if condition(element) ] print(filtered)
Code language: PHP (php)

Видно, что в данном случае мы экономим несколько строк кода. Результат выполнения этого примера следующий:

[3, 4, 2, 8, 9, 11, 10]
Code language: JSON / JSON with Comments (json)

Аналогично, если исходным набором данных является не список, а кортеж, множество или, вообще, любой итерируемый элемент, наш код будет работать точно так же.

Рассмотрим пример, когда, начиная с кортежа с различными элементами, я хочу сохранить только пары без повторений, поэтому вместо списка я создам набор (наборы не допускают повторений).

def en_pair(num): return not num % 2 tuple = (4, 3, 2, 6, 7, 2, 11, 2, 4, 13, 27, 2, 1) set = { element for element in tuple if en_pair(element) } print(set)
Code language: JavaScript (javascript)

Примечание: в исходном кортеже у меня есть несколько повторяющихся чисел и несколько нечетных чисел. Конечный результат должен содержать только четные числа и не иметь повторений. Если вы выполните приведенный выше код, то получите следующее:

{2, 4, 6}

Давайте рассмотрим другой, еще лучший способ фильтрации, который заключается в использовании собственной функцией filter в Python.

Как отфильтровать список с помощью функции filter

Функция filter возвращает итерируемый объект со всеми значениями коллекции данных, которые удовлетворяют определенному условию. Он должен быть вызван с двумя параметрами: функцией, определяющей, удовлетворяет ли данный элемент условию, и коллекцией данных для фильтрации: filter(function, data).

Как видите, это более удобный способ добиться фильтрации списка или вообще любой последовательности, итерируемого элемента или итератора.

Обратите внимание, что вызов функции filter возвращает объект, а не непосредственно список или последовательность, к которым можно получить доступ по индексам. Этот объект, относящийся к классу filter, является итерируемым объектом. Это означает, что вы можете последовательно обращаться к его элементам с помощью функции next. Вы также можете пройтись по нему с помощью цикла for.

Интересно то, что если вы хотите получить список, кортеж или набор из объекта фильтра, вы можете сделать это, вызвав функции list, tuple или set, соответственно, на результате фильтрации.

Функция filter получает два параметра:

  • Первый должен быть функцией или вызываемым элементом, который может принимать параметр. Эта функция будет вызываться последовательно в цикле, где в каждой итерации в качестве аргумента предоставляется один из элементов списка. Эта функция фильтрации, которую мы будем называть condition, должна возвращать булево значение, True или False, указывающее на то, должен ли элемент, полученный параметром, быть в отфильтрованном списке.
  • Второй параметр должен быть коллекцией элементов и может быть последовательностью или любым итерируемым элементом, таким как список или кортеж.

Давайте посмотрим, как получить отфильтрованный список из списка, содержащего только элементы, кратные 2, 3 или 11:

def condition(num): return not num % 2 or not num % 3 or not num % 11 list = [3, 1, 4, 7, 2, 8, 9, 11, 10] # список для фильтрации filtered = list(filter(condicion, list)) # вызываем фильтр, а затем список print(filtrada) # выводим отфильтрованный список
Code language: PHP (php)

Результат будет выглядеть следующим образом:

[3, 4, 2, 8, 9, 11, 10]
Code language: JSON / JSON with Comments (json)

Просто, не так ли?

Если мы оказались в ситуации, когда условие для фильтра простое, нам не нужно создавать отдельную функцию условия, и мы можем использовать лямбда-выражения.

Лямбда-выражения позволяют нам создавать безымянные функции, которые выполняют простую операцию для заданных аргументов и возвращают результат операции.

Например, я могу создать лямбда-выражение, которое сообщает мне, является ли число четным, следующим образом. Кроме того, я присваиваю это выражение переменной, чтобы назвать функцию и использовать ее, когда захочу.

Это возможно, поскольку функции являются элементами первого порядка в Python (что позволяет вам присваивать их переменной, как я уже говорил, или предоставлять их в качестве аргумента другим функциям):

en_pair = lambda x: not x % 2 print(en_pair(2)) print(en_pair(7)) print(en_pair(12))
Code language: PHP (php)

Результат будет следующим:

True False True
Code language: PHP (php)

С помощью лямбда-выражения мы можем фильтровать список без необходимости создавать функцию условия фильтрации.

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

list = [3, 1, 4, 7, 2, 8, 9, 11, 10] filtered = tuple(filter(lambda x: not x % 2, list)) # фильтрация с помощью лямбда-выражения print(filtered)
Code language: PHP (php)

На этот раз мы сгенерировали кортеж, поэтому результат на экране будет выглядеть следующим образом:

(4, 2, 8, 10)

Вы видите, насколько удобной, простой и быстрой может быть фильтрация с помощью функции filter. Но… это еще не все!

Как отфильтровать нулевые элементы из списка

Можно дать функции filter значение None. Таким образом, мы получим отфильтрованный список, в котором будут исключены те элементы, которые в Python оцениваются как False (например, False, 0, ” или []).

Так, например, если мы хотим удалить все нули из списка целых чисел, мы просто должны сделать следующее:

list = [2, 0, 0, 4, 6, 1, 0, 0, 0, 3, 0] filtered = list(filter(None, list)) # фильтрация с помощью None print(filtered)
Code language: PHP (php)

Получаем следующий результат:

[2, 4, 6, 1, 3]
Code language: JSON / JSON with Comments (json)

Однако, как я уже сказал, таким образом мы можем удалить из отфильтрованного списка любой элемент, который оценивается как False:

list = [None, False, True, 0, 1, 'text', '', [10, 11, 12], [], {10, 11, 12}, {}, (10, 11, 12), ()] filtered = list(filter(None, list)) # фильтрация с помощью None print(filtered)
Code language: PHP (php)

Это даст нам следующий результат:

[True, 1, 'text', [10, 11, 12], {10, 11, 12}, (10, 11, 12)]
Code language: PHP (php)

Как получить элементы, удаленные с помощью фильтрации

Бывают случаи, когда помимо отфильтрованного списка нам нужен второй список с элементами, которые были удалены во время фильтрации. Другими словами, нам нужно инвертировать условие фильтрации, чтобы удалить те элементы, которые не удовлетворяют условию.

Функция filter не позволяет сделать это напрямую, т.е. нужно будет создать обратное условие вручную. Но Python уже учитывает это и предоставляет нам функцию filterfalse, находящуюся в модуле itertools.

Итак, если мы хотим отфильтровать список, чтобы сохранить все элементы, кратные 2, 3 или 11, и, кроме того, нам нужен другой список с исключенными элементами, то есть теми, которые не кратны 2, 3 или 11, мы можем сделать следующее:

from itertools import filterfalse # импортируем функцию def condition(num): return not num % 2 or not num % 3 or not num % 11 data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] filtered = list(filter(condition, data)) # фильтруем список eliminated = list(filterfalse(condition, data)) # фильтруем список с обратным условием print(f'Отфильтрованный список {filtered}') print(f'Удаленные элементы {eliminated}')
Code language: PHP (php)

Таким удобным способом мы получаем отдельно элементы, которые выполняют условие, от тех, которые не выполняют:

Отфильтрованный список [2, 3, 4, 6, 8, 9, 10, 11, 12, 14, 15] Удаленные элементы [1, 5, 7, 13]
Code language: CSS (css)

Как отфильтровать список объектов

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

Например, мы будем работать с треугольниками. Для них мы создадим класс Triangle с тремя параметрами, которые представляют собой длину в миллиметрах каждой из трех сторон. Кроме того, мы включим функцию, возвращающую площадь треугольника, и еще одну, возвращающую периметр.

Также, чтобы быть немного формальными, мы не будем строить треугольник, если его стороны неправильные, что может произойти, если значение некоторых из них равно нулю или меньше, или если сумма двух сторон меньше значения третьей стороны.

Давайте посмотрим на код:

from math import sqrt class Triangle: def __init__(self, side_a, side_b, side_c): if side_a <= 0 or side_b <= 0 or side_c <= 0 or \ side_a + side_b <= side_c or \ side_b + side_c <= side_a or \ side_a + side_c <= side_b: raise ValueError('Треугольник с неправильными сторонами.') self.side_a = side_a self.side_b = side_b self.side_c = side_c def __str__(self): return f'Треугольник: {self.side_a} {self.side_b} {self.side_c}, площадь {self.area()}, периметр {self.perimeter()}.' def area(self): # применим формулу Герона sp = self.perimeter() / 2 return sqrt(sp * (sp - self.side_a) * (sp - self.side_b) * (sp - self.side_c)) def perimeter(self): return self.side_a + self.side_b + self.side_c

Чтобы вычислить периметр по сторонам треугольника, просто суммируем их. Чтобы вычислить площадь треугольника по величине его сторон, можно применить формулу Герона.

Теперь мы можем легко создать треугольники следующим образом:

t1 = Triangle(2, 3, 4) t2 = Triangle(17, 21, 25) t3 = Triangle(0.5, 0.3, 0.47) print(t1, t2, t3, sep='\n')
Code language: PHP (php)

Это даст нам следующий результат:

Треугольник: 2 3 4, площадь 2.9047375096555625, периметр 9. Треугольник: 17 21 25, площадь 176.5593030684025, периметр 63. Треугольник: 0.5 0.3 0.47, площадь 0.06883639571476707, периметр 1.27.
Code language: CSS (css)

С этим все понятно, давайте рассмотрим пример, в котором мы хотим отфильтровать список объектов класса Triangle.

Предположим, что мы хотим отфильтровать список треугольников, чтобы оставить только те, у которых максимальная площадь равна 10, а минимальный периметр – 15.

Для этого мы создаем несколько треугольников и добавляем их в список. Затем производим фильтрацию с помощью лямбда-выражения, в котором выполняем сравнение с указанными значениями и делаем соответствующие вызовы функций площади и периметра. Наконец, мы выводим полученные отфильтрованные треугольники на экран:

# создаем несколько треугольников и добавляем их в список triangles = [] triangles.append(Triangle(10, 7, 6)) triangles.append(Triangle(5, 4, 6)) triangles.append(Triangle(7, 8, 4)) triangles.append(Triangle(7, 9, 5)) triangles.append(Triangle(4, 4, 4)) triangles.append(Triangle(4, 5, 8)) print('Треугольники:') for i, t in enumerate(triangles): print(f' {i}: {t}') filtered = list(filter(lambda t: t.area() <= 10 and t.perimeter() >= 15, triangles)) print('Отфильтрованные треугольники:') for i, t in enumerate(filtered): print(f' {i}: {t}')
Code language: PHP (php)

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

Я выбрал способ отображения треугольников на экране – использовать собственную функцию Python enumerate, которая позволяет получить индекс каждого треугольника в списке, чтобы отобразить и его.

Вывод, сгенерированный приведенным выше кодом, выглядит следующим образом, где видно, что действительно, отфильтрованными треугольниками являются те, которые удовлетворяют условиям, заданным для максимальной площади 10 и минимального периметра 15:

Треугольники: 0: Треугольник: 10 7 6, площадь 20.662465970933866, периметр 23. 1: Треугольник: 5 4 6, площадь 9.921567416492215, периметр 15. 2: Треугольник: 7 8 4, площадь 13.997767679169419, периметр 19. 3: Треугольник: 7 9 5, площадь 17.41228014936585, периметр 21. 4: Треугольник: 4 4 4, площадь 6.928203230275509, периметр 12. 5: Треугольник: 4 5 8, площадь 8.181534085976786, периметр 17. Отфильтрованные треугольники: 0: Треугольник: 5 4 6, площадь 9.921567416492215, периметр 15. 1: Треугольник: 4 5 8, площадь 8.181534085976786, периметр 17.
Code language: CSS (css)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *