Функция фильтрации Python в цикле for

#python

#python

Вопрос:

У меня возникла проблема при использовании функции filter в цикле for. 2 случая похожи, но результат разный:

 nums = list(range(1, 15))
nums = filter(lambda x: x % 2 == 0, nums)
nums = filter(lambda x: x % 3 == 0, nums)
nums = filter(lambda x: x % 4 == 0, nums)
print(list(nums))

>>> [12]
 

и

 nums = list(range(1, 15))
for i in range(2, 5):
    nums = filter(lambda x: x % i == 0, nums)
print(list(nums))

>>> [4, 8, 12]
 

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

 nums = list(range(1, 15))
for i in range(2, 5):
    nums = list(filter(lambda x: x % i == 0, nums))
print(nums)

>>> [12] 
 

Есть ли какое-либо решение, использующее цикл for без преобразования объекта фильтра в список в этом случае?

Комментарии:

1. nums = filter(lambda x, i=i: x % i == 0, nums)

Ответ №1:

filter возвращает генератор, поэтому вы получаете список только после передачи генератора list() , который принимает все сгенерированные элементы и возвращает их в виде списка.

Способ получить то, что вы хотите, без filter() использования for :

 nums = list(range(1, 15))
result = [x for x in nums for i n range(2, 5) if x % i == 0]
 

Это называется пониманием списка, и это очень эффективный и понятный способ построения подобного списка.

Комментарии:

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

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

Ответ №2:

Фильтр — это генератор. Поэтому он использует отложенное вычисление выражения. Из документации:

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

Это означает, что лямбда-выражение вычисляется при вызове list(nums) , потому что оно вызывает __next__() метод под капотом.

Итак, во втором примере он будет (я думаю) фильтровать 3 раза всегда с разделителем 4:

 nums = filter(lambda x: x % 4 == 0)
nums = filter(lambda x: x % 4 == 0)
nums = filter(lambda x: x % 4 == 0)
 

Возможно, этот фрагмент кода дает вам лучшее понимание. Обратите внимание, что выражение вычисляется при list() вызове. Как вы можете видеть, цикл здесь не изменяет результат. Использование переменной i имеет значение:

 nums = list(range(1, 15))
i = 2
nums = filter(lambda x: x % i == 0, nums)
i = 3
nums = filter(lambda x: x % i == 0, nums)
i = 4
nums = filter(lambda x: x % i == 0, nums)
print(list(nums)) # here i==4
### [4, 8, 12]

nums = list(range(1, 50))
for i in range(2, 5):
   nums = filter(lambda x: x % i == 0, nums)
i = 11
print(list(nums)) # here i==11
### [11, 22, 33, 44]
 

Еще одно решение:

 def f(x):
   for i in range(2, 5):
      if x % i != 0:
         return False
   return True

nums = list(range(1, 15))
nums = filter(f, nums)
print(list(nums))