ttn
ttn

Reputation: 13

Python filter function in for loop

I got an issue when using filter-function in for loop. 2 cases are similar, but the result is different:

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]

and

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]

If I convert filter-object to list, the result is correct.

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

>>> [12] 

Is there any solution using for loop without converting filter object to list in this case?

Upvotes: 1

Views: 3627

Answers (2)

mateuszl1995
mateuszl1995

Reputation: 173

Filter is generator. Therefore it uses lazy evaluation of expression. From documentation:

Variables used in the generator expression are evaluated lazily when the __next__() method is called for the generator object (in the same fashion as normal generators).

It means that lambda expression is evaluated when you call list(nums) because it calls __next__() method under the hood.

So in your second example it will (I guess) filter 3 times always with divider 4:

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

Maybe that piece of code gives you better understanding. Notice that expression is evaluated when list() is called. As you can see, loop here doesn't change the result. Using variable i makes the difference:

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]

One more solution:

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))

Upvotes: 1

Grismar
Grismar

Reputation: 31329

filter returns a generator, which is why you only obtain a list after passing the generator to list(), which takes all the elements generated and returns them in a list.

A way to get what you want without filter() and using for:

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

This is called a list comprehension and it's very efficient and readable way of constructing a list like this.

Upvotes: 4

Related Questions