1

I am confuse about the behaviour of the filter function in Python 3.x assuming the next code:

>>> test = [1, 2, 3, 4, 5, 6, 7, 8]
>>> for num in range(4):
       test = filter( lambda x: x != num, test)
>>> print(list(test))
    # [1, 2, 4, 5, 6, 7, 8]

I was thinking that the test variable will contain the result of successively filtering the values (num) present in range(4), nevertheless the final list is not filtered at all !.

Can someone explain to me this behavior, please? And if possible how to get the expected result # [4, 5, 6, 7, 8]

Note: My original code isn't so simple as this one, but this is just to illustrate the point where I found my bug.

1
  • I've just run your code and it works perfectly on python2.7. Are you sure it does not work in python 3? Commented May 10, 2015 at 16:31

3 Answers 3

2

The problem is that filter returns an iterator, and the value of num is not "frozen" by the iterator, as shown by the following code:

>>> test = [1, 2, 3, 4, 5, 6, 7, 8]
>>> for num in range(4):
...     test = filter(lambda x: print(x, '!=', num) or x != num, test)
... 
>>> list(test)
1 != 3
1 != 3
1 != 3
1 != 3
2 != 3
2 != 3
2 != 3
2 != 3
3 != 3
4 != 3
[...]
[1, 2, 4, 5, 6, 7, 8]

As you can see, when list(test) and the iterator is evalutated, only the last value of num is used.

One solution may be using list(filter(...)) in every iteration, as it has already been proposed.

But if you want to save memory, here's how you could "freeze" num:

>>> import functools
>>> test = [1, 2, 3, 4, 5, 6, 7, 8]
>>> not_equal = lambda x, y: x != y
>>> for num in range(4):
...     test = filter(functools.partial(not_equal, num), test)
... 
>>> list(test)
[4, 5, 6, 7, 8]

(Of course, this is just an example. Try to make your code more readable.)

In general, what you have to do is keep a reference to the value of num and avoid referencing it by name in the inner scope.

Sign up to request clarification or add additional context in comments.

Comments

0

The problem is that filter returns an instance of a filter object. For the code you wrote to work, you have to use the filter object to create a new list in the loop

for num in range(4):
    test = list( filter( lambda x: x != num, test) )

Comments

0

The filter object still contains every object in test. Thus, try the following:

test = [1, 2, 3, 4, 5, 6, 7, 8]
for num in range(4):
   test = filter( lambda x: x != num, list(test))
print(list(test))

By casting list to test in the 3rd line, we can effectively remove the items we do not want:

>>> print(list(test))
[4, 5, 6, 7, 8]
>>>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.