FULL PYTHON FIX · 8 MIN READ

How to Filter a List in Python: Comprehension vs filter()

Use a list comprehension when you need a list immediately. filter() applies a predicate lazily, so convert its iterator to a list only when you need the whole result.

Before you start: Lists · Conditions · Functions

Filter a list and keep a list

Put the condition after the for clause in a list comprehension. The result is a new list in the original order; the input list stays intact.

PYTHON
numbers = [-2, 0, 3, 4, 7, 8]
positive_even = [n for n in numbers if n > 0 and n % 2 == 0]
print(positive_even)
print(numbers)

Expected result: [4, 8] [-2, 0, 3, 4, 7, 8]

See why this works and test the edge cases

Video guide: How to Filter a List in Python: Comprehension vs filter()

The preview is stored on this site. YouTube loads only when you press Play. Watch on YouTube

Read the video transcript

Select a timestamp to watch that moment on YouTube.

  1. 00:00

    We have a Python list containing negative numbers, zero, odd numbers, and positive even numbers. Our job is to create a new list with only the positive even values, in their original order. Four and eight should survive. Zero is even, but it is not positive, so it must not appear. The original input list should remain unchanged. This sounds like a one-line problem, and it can be, but two common implementations behave differently: a list comprehension creates a list immediately, while Python's built-in filter returns an iterator. That difference matters when the next line calls len, indexes the result, or tries to use the same object twice.

  2. 00:43

    We will run a broken example, read the actual error, compare the two forms, and verify the cases that most often hide a mistake. Keep this input in mind and predict each output before it appears in the terminal. The matching web guide includes runnable examples and a checked practice task, so you can repeat every step after the video. The predicate itself states the rule correctly. For each n, it checks n greater than zero and remainder zero when divided by two. But the line assigning selected does not build the list. In current Python, filter returns an iterator that can produce matching values as it is consumed.

  3. 01:24

    The next line asks len for the size of that iterator as though it were a list. The TypeError names the actual object: filter. The failure is about the result type, not whether four or eight matches. A tempting reaction is to print selected and see what is inside. That prints a filter-object representation with a memory address, not the selected numbers. It is more useful to ask what our function promises to return. If a caller needs a list that it can count, index, or revisit, we must build that list. If a caller can consume a stream once, a filter iterator can be useful. Do not change the selection rule just because the container type is wrong.

  4. 02:07

    We will keep the predicate and inspect what consumption does before choosing the final implementation. The first list call consumes the filter iterator and produces the two expected values. The second call consumes the same object after it has already reached the end, so it produces an empty list. The input numbers list has not changed. This shows three separate facts: filter selected the correct items, the iterator was lazy until consumed, and it cannot simply be reused after consumption. If you need to traverse the matches twice, store the first converted list or create a new filter iterator from the input.

  5. 02:47

    A list comprehension takes a different route: it performs the condition while building a new list immediately. Neither tool automatically removes duplicates or sorts the result. Both preserve the order of items they keep. For this task, returning a real list is part of the contract, so a comprehension is concise and direct. A named predicate with `list(filter(...))` is also valid when that name makes a reused rule easier to read. The key is to choose deliberately rather than treating an iterator and a list as interchangeable. Read the comprehension as “put n in the new list, for each n in numbers, if n passes this condition.” The if at the end filters; it does not replace or transform

  6. 03:30

    the value. The condition has two parts. Greater than zero excludes negative values and zero. Remainder zero excludes odd values. The returned object is already a list, so callers can take its length or use an index. In the next print, filter applies the named is_positive_even function and list converts the resulting iterator. It produces exactly the same two numbers. The final print proves neither form changed the original input. If a function promises a list, do not return a bare filter object and leave the surprise for its caller.

  7. 04:10

    If you only need to process matches once, the lazy iterator may avoid creating an intermediate list, but that is a different contract. Here the list comprehension communicates both selection and result shape in one expression. This does not mean every predicate belongs in a long one-liner: when the rule gets complicated, name a function or split the computation so it remains understandable. Empty input returns an empty list, and repeated matching twos remain repeated. Filtering selects items; it does not deduplicate them. The assertions also confirm that the function returns only four and eight for the sample input while leaving the caller's list untouched.

  8. 04:53

    Now notice a different use of filter: passing None in place of the predicate removes every false-looking value. That includes zero, which may be a valid measurement or balance, as well as an empty string or False. In this run, the first result loses zero. The explicit `is not None` comprehension removes only the missing marker and keeps the meaningful zero. This is why an explicit condition matters more than the choice between comprehension and filter. For our positive-even task, zero disappears because it fails `n > 0`, not merely because it is false-looking. The practice task on the web checks return type, order, duplicates, zero, empty

  9. 05:34

    input, and unchanged input. Implement either a comprehension or `list(filter(...))`, run the checks, and then add another positive even number to make sure your rule generalizes beyond this one sample. When you meet a filter object error later, check whether you promised a list but returned an iterator. When values vanish unexpectedly, inspect the predicate rather than changing syntax blindly.

Why does filter() not give me a list?

Suppose a report needs only positive even numbers from a list. The built-in filter() function looks promising: pass a predicate that returns True for values to keep, then pass the input iterable. In modern Python, however, filter() returns an iterator rather than an already built list. A common follow-up, len(result), therefore raises TypeError. The predicate is not the problem; the expectation about the result type is.

Run the example and read the last line of the traceback. Avoid printing the filter object itself as your check, because its memory address is not a useful answer and changes from run to run. Decide first whether the caller needs a concrete list or whether it will consume matching items one at a time. That decision determines which form is clearest.

Calling len() on a filter iterator
PYTHON
numbers = [-2, 0, 3, 4, 7, 8]
selected = filter(lambda n: n > 0 and n % 2 == 0, numbers)
print(len(selected))
ERROR (LAST LINE)
TypeError: object of type 'filter' has no len()

Compare the two result types and their timing

A list comprehension evaluates its condition as it builds a new list. The if at the end is a filter: an item is copied into the result only when the condition is true. It preserves the order and duplicates of surviving items. Calling filter(predicate, numbers) represents the same selection lazily. It evaluates the predicate while the returned iterator is consumed. Wrapping it in list(...) builds the complete list, so len() and indexing then work as they do for any other list.

An iterator is consumed as you read it. If you turn the same filter object into a list twice, the second list is empty. Create a fresh filter iterator or store the first converted list if you need to reuse the result. Neither approach changes the original numbers list. The usual reason to choose filter() is that an existing named predicate reads clearly, or that a consumer can process matches incrementally without storing them all.

The comprehension directly returns a list
PYTHON
numbers = [-2, 0, 3, 4, 7, 8]
selected = [n for n in numbers if n > 0 and n % 2 == 0]
print(selected)
print(len(selected))
print(numbers)
OUTPUT
[4, 8]
2
[-2, 0, 3, 4, 7, 8]
A filter iterator is used once
PYTHON
numbers = [-2, 0, 3, 4, 7, 8]
selected = filter(lambda n: n > 0 and n % 2 == 0, numbers)
print(list(selected))
print(list(selected))
OUTPUT
[4, 8]
[]

Write the condition explicitly

For a function whose contract says return a list, use a list comprehension with an explicit predicate. Here positive means greater than zero; even means the remainder after division by two is zero. Zero is even, but it is not positive, so it must be excluded. Python checks both parts of the condition for each value, keeps 4 and 8 in their original order, and returns a new list. This answer is easy to test with len(), equality, and indexing.

The filter() version is equally valid when wrapped with list(...). A named is_positive_even function may communicate the rule more clearly than a lambda if the rule is reused elsewhere. Avoid changing the condition just to fit a particular syntax: both forms should produce the same items for the same inputs. If an upstream source is a generator and the consumer only needs one pass, keeping filter() as an iterator can avoid building an intermediate list.

Return a list with a comprehension
PYTHON
def positive_evens(numbers):
    return [n for n in numbers if n > 0 and n % 2 == 0]

values = [-2, 0, 3, 4, 7, 8]
print(positive_evens(values))
print(values)
OUTPUT
[4, 8]
[-2, 0, 3, 4, 7, 8]
Build the same list from filter()
PYTHON
def is_positive_even(n):
    return n > 0 and n % 2 == 0

numbers = [-2, 0, 3, 4, 7, 8]
print(list(filter(is_positive_even, numbers)))
OUTPUT
[4, 8]

Test empty input, duplicates, and false-looking values

Verify the rule with more than the successful example. Empty input should return an empty list. Duplicate matching values should remain duplicates, because filtering selects items rather than deduplicating them. The result must remain a list, and the original input must stay unchanged. These cases catch shortcuts that accidentally convert the answer to a set or mutate the list while iterating over it.

Be especially careful with filter(None, values). Passing None as the predicate removes every false-looking value, including 0, False, and empty strings. That is useful only when truthiness is the rule you actually want. It is not a replacement for an explicit numeric condition, and it could silently discard meaningful zeros from measurements or balances. When the data can mix types, define and validate the accepted type before comparing values; the exercise here assumes integers.

Check boundaries and duplicates
PYTHON
def positive_evens(numbers):
    return [n for n in numbers if n > 0 and n % 2 == 0]

for values in ([], [0, -2, 2, 2, 5], [7, 9]):
    print(positive_evens(values))
OUTPUT
[]
[2, 2]
[]
filter(None, ...) removes all false-looking values
PYTHON
measurements = [0, 5, None, 8]
print(list(filter(None, measurements)))
print([value for value in measurements if value is not None])
OUTPUT
[5, 8]
[0, 5, 8]
  • Use a list comprehension when the caller needs a list now.
  • Convert filter() with list(...) only when you want the complete lazy result.
  • Write an explicit condition when zeros or other false-looking values must be preserved.

Practice the fix

Write positive_evens(numbers) to return a new list containing only positive even integers in their original order. Preserve duplicates, return [] for empty input, and leave the caller's list unchanged. Use either a comprehension or list(filter(...)).

Need a hint?

Keep n only if n > 0 and n % 2 == 0. A comprehension already returns a list; filter() needs list(...) when a list is required.

See the failure, fix it, then test the boundary

Load each step into the console below and select Run code. Your draft can be restored after trying a step. Loading a sample does not complete the practice task.

  1. 1 · Reproduce the problem

    Expected: TypeError: object of type 'filter' has no len()

  2. 2 · Run the repair

    Expected: [4, 8], then 2

  3. 3 · Test an edge case

    Expected: An empty list, zero, duplicates, and unchanged input all pass.

Stuck? Reveal one hint at a time.
0 of 3 hints shown

Need the concept first? Study List Comprehensions and its checked task

Python console

Ready to run

Edit the code and run it in your browser. Examples above can be loaded with Try this example.

Does your code use input()? Add one value per line
Ctrl / ⌘ + Enter to run
OUTPUT
Your output appears here.

Write your solution, then select Check practice.

    Key takeaways

    • A comprehension with a trailing if builds a new filtered list immediately.
    • filter() returns an iterator; use list(filter(...)) when your function promises a list.
    • A filter iterator is consumed as it is read and cannot simply be reused.
    • filter(None, values) removes every false-looking item, including meaningful zeros.

    What to do next

    1. Review the prerequisite: Lists.
    2. Prove it yourself: Pass the checked task in List Comprehensions.

    Related lessons

    Sources and further reading