ZetCode

Python filter list

last modified October 30, 2026 Python 3.15

In this article we show how to filter lists in Python.

Filtering

A filtering operation processes a data structure, such as a list, and produces a new data structure containing exactly those elements for which the given predicate returns true.

A predicate is a single-argument function which returns a boolean value. In Python, any value that is true in a boolean context counts as true.

The most common tools for filtering lists are list comprehensions and the built-in filter function.

Filtering with a list comprehension

A list comprehension is a syntactic construct which creates a list based on an existing iterable. A for clause goes through the sequence; an optional if clause decides which elements are included.

positive.py
vals = [-2, 1, -4, 2, 0, -1, 12, -3]

positive = [e for e in vals if e > 0]
print(positive)

We have a list of integers. We create a new list of positive integers.

positive = [e for e in vals if e > 0]

The condition is applied on each element; an element is included in the new list only if it satisfies the condition. The original list is not modified.

$ python positive.py
[1, 2, 12]

Odd and even numbers

Even numbers are integers that are exactly divisible by two; odd numbers leave a remainder of one.

odd_values.py
vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

odd = [e for e in vals if e % 2]
even = [e for e in vals if e % 2 == 0]

print(odd)
print(even)

The example creates filtered lists of odd and even values with the % operator. For odd numbers, the remainder is 1, which is true; for even numbers, it is 0, which is false.

$ python odd_values.py
[1, 3, 5, 7, 9, 11]
[2, 4, 6, 8, 10, 12]

Filtering by type

We can filter values by their data types.

by_type.py
vals = ['a', 4, 'c', 12, 'e', 3.5, True, 'd', None]

ints = [e for e in vals if isinstance(e, int)]
strict_ints = [e for e in vals if isinstance(e, int) and not isinstance(e, bool)]
strs = [e for e in vals if isinstance(e, str)]
numbers = [e for e in vals if isinstance(e, (int, float)) and not isinstance(e, bool)]

print(ints)
print(strict_ints)
print(strs)
print(numbers)

In the example, we have a list that is a mix of integers, strings, a float, a boolean, and None. We separate the values by type with the isinstance function.

strict_ints = [e for e in vals if isinstance(e, int) and not isinstance(e, bool)]

The bool type is a subclass of int, so True passes the isinstance(e, int) test. To exclude booleans, we add another condition. The isinstance function accepts a tuple of types.

$ python by_type.py
[4, 12, True]
[4, 12]
['a', 'c', 'e', 'd']
[4, 12, 3.5]

Multiple conditions

We can have multiple conditions in a list comprehension.

multiple_conditions.py
vals = [9, -2, 5, 14, 22, -11, 7, -19, 23, 10]

res1 = [e for e in vals if e % 2 == 0 if e > 0]
res2 = [e for e in vals if e % 2 == 0 and e > 0]
res3 = [e for e in vals if e < 0 or e > 20]
res4 = [e for e in vals if 0 < e < 10]

print(res1)
print(res2)
print(res3)
print(res4)

Two consecutive if clauses are equivalent to one condition joined with and; the latter form is more common. With or, we keep elements that satisfy any of the conditions. Chained comparisons express ranges concisely.

$ python multiple_conditions.py
[14, 22, 10]
[14, 22, 10]
[-2, 22, -11, -19, 23]
[9, 5, 7]

The filter function

The filter built-in function returns an iterator over those elements of an iterable that satisfy the given predicate.

filter_fun.py
words = ['sky', 'cloud', 'wood', 'forest', 'tie', 'nice', 'cup']

filtered = filter(lambda e: len(e) == 3, words)
print(filtered)
print(list(filtered))
print(list(filtered))

With the filter function, we find all words that have three characters.

filtered = filter(lambda e: len(e) == 3, words)

As a predicate, we have an anonymous function which checks the length of the current element.

print(filtered)
print(list(filtered))
print(list(filtered))

The filter function returns a lazy iterator; to view the elements, we pass it to the list function. An iterator can be consumed only once, so the second list call returns an empty list.

$ python filter_fun.py
<filter object at 0x7e2e58a0fb50>
['sky', 'tie', 'cup']
[]

Named predicates

The filter function reads best with a named function. Methods of built-in types can be used as predicates directly.

filter_named.py
def is_palindrome(word):
    return word == word[::-1]


words = ['level', 'forest', 'radar', 'sky', 'kayak', 'cup', 'noon']

print(list(filter(is_palindrome, words)))
print([word for word in words if is_palindrome(word)])

codes = ['12', 'x7', '305', '', '4a', '99']
print(list(filter(str.isdigit, codes)))

We find the palindromes in a list of words with filter and with a list comprehension. The str.isdigit method keeps the strings consisting only of digits; an empty string is not included.

$ python filter_named.py
['level', 'radar', 'kayak', 'noon']
['level', 'radar', 'kayak', 'noon']
['12', '305', '99']

Removing falsy values

If the predicate is None, filter removes all elements that are false.

filter_none.py
vals = [0, 1, '', 'sky', None, [], [0], False, 2.5, {}]

print(list(filter(None, vals)))
print([e for e in vals if e])

lines = ['first', '', 'second', '   ', 'third']
print([line for line in lines if line.strip()])

Zero, empty strings, None, empty collections, and False are removed. A list containing zero is not empty, so it is kept. To drop blank lines, we test the stripped line.

$ python filter_none.py
[1, 'sky', [0], 2.5]
[1, 'sky', [0], 2.5]
['first', 'second', 'third']

Filtering strings

String methods are useful in filtering conditions.

filter_strings.py
words = ['Python', 'perl', 'Go', 'PHP', 'Rust', 'pascal', 'C#', 'Prolog']

print([w for w in words if w.startswith('P')])
print([w for w in words if w.lower().startswith('p')])
print([w for w in words if 'o' in w])
print([w for w in words if len(w) > 4 and w[0].isupper()])

We find words starting with P, with a case-insensitive variant, words containing the letter o, and capitalized words longer than four characters.

$ python filter_strings.py
['Python', 'PHP', 'Prolog']
['Python', 'perl', 'PHP', 'pascal', 'Prolog']
['Python', 'Go', 'Prolog']
['Python', 'Prolog']

Filtering dictionaries

Data often comes as a list of dictionaries.

filter_dicts.py
users = [
    {'name': 'John Doe', 'occupation': 'gardener', 'age': 34},
    {'name': 'Roger Roe', 'occupation': 'driver', 'age': 19},
    {'name': 'Lucy Smith', 'occupation': 'teacher', 'age': 46},
    {'name': 'Paul Novak', 'occupation': 'driver', 'age': 28},
]

drivers = [u['name'] for u in users if u['occupation'] == 'driver']
print(drivers)

older = [u for u in users if u['age'] > 30]
for user in older:
    print(user)

prices = {'apple': 1.2, 'banana': 0.5, 'cherry': 4.0, 'plum': 2.1}
print({k: v for k, v in prices.items() if v < 2})

We get the names of the drivers and the users older than thirty. The condition and the expression of the comprehension are independent, so we can filter by one key and collect another. The last line filters a dictionary with a dictionary comprehension.

$ python filter_dicts.py
['Roger Roe', 'Paul Novak']
{'name': 'John Doe', 'occupation': 'gardener', 'age': 34}
{'name': 'Lucy Smith', 'occupation': 'teacher', 'age': 46}
{'apple': 1.2, 'banana': 0.5}

Filtering objects

Objects are filtered by their attributes.

filter_objects.py
from dataclasses import dataclass


@dataclass
class Product:
    name: str
    price: float
    in_stock: bool


products = [
    Product('keyboard', 49.9, True),
    Product('mouse', 19.9, False),
    Product('monitor', 189.0, True),
    Product('cable', 4.5, True),
]

available = [p for p in products if p.in_stock]
cheap = [p.name for p in available if p.price < 50]

print(len(available))
print(cheap)

We first select the products in stock and then the cheap ones among them.

$ python filter_objects.py
3
['keyboard', 'cable']

Partitioning a list

Sometimes we need both the matching and the non-matching elements. The itertools.filterfalse function returns the elements for which the predicate is false.

partition.py
from itertools import filterfalse

vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


def is_even(n):
    return n % 2 == 0


print(list(filter(is_even, vals)))
print(list(filterfalse(is_even, vals)))

passed, failed = [], []

for score in [72, 45, 90, 38, 66, 51]:
    (passed if score >= 50 else failed).append(score)

print(passed, failed)

Calling filter and filterfalse goes through the list twice. A loop does it in one pass: the conditional expression selects the list to append to.

$ python partition.py
[2, 4, 6, 8, 10]
[1, 3, 5, 7, 9]
[72, 90, 66, 51] [45, 38]

The takewhile and dropwhile functions

The takewhile function returns elements as long as the predicate is true and stops at the first false one; dropwhile skips elements while the predicate is true and returns the rest.

take_drop.py
from itertools import takewhile, dropwhile

temps = [3, 5, 8, 12, 15, 11, 7, 4]

print([t for t in temps if t < 10])
print(list(takewhile(lambda t: t < 10, temps)))
print(list(dropwhile(lambda t: t < 10, temps)))

The list comprehension keeps all temperatures below ten. The takewhile function stops at 12, so the later values 7 and 4 are not included; dropwhile returns everything from 12 on. These functions are useful for sorted data or for the leading part of a stream.

$ python take_drop.py
[3, 5, 8, 7, 4]
[3, 5, 8]
[12, 15, 11, 7, 4]

Finding the first match

To find only the first matching element, we do not need to filter the whole list.

first_match.py
vals = [7, 13, 22, 9, 40, 18]

first_even = next((e for e in vals if e % 2 == 0), None)
print(first_even)

first_big = next((e for e in vals if e > 100), None)
print(first_big)

print(any(e > 30 for e in vals))

The next function takes the first element of a generator expression; the second argument is returned when no element matches. The generator stops at the first match. To only check whether some element matches, we use any.

$ python first_match.py
22
None
True

Filtering in place

List comprehensions and filter create new lists. Removing elements from a list while iterating over it is a common bug.

in_place.py
vals = [1, 2, 2, 3, 2, 4]

for e in vals:
    if e == 2:
        vals.remove(e)

print(vals)

vals = [1, 2, 2, 3, 2, 4]
alias = vals

vals[:] = [e for e in vals if e != 2]

print(vals, alias)

The loop skips an element after each removal, because the following elements shift one position to the left; one 2 is left in the list. To filter a list in place, we assign the filtered list to the full slice. The list object is kept, so other references to it see the change.

$ python in_place.py
[1, 3, 2, 4]
[1, 3, 4] [1, 3, 4]

Removing duplicates

A filter can remove duplicates while preserving the order of the elements.

unique_values.py
words = ['sky', 'cup', 'sky', 'wood', 'cup', 'forest', 'sky']

print(list(dict.fromkeys(words)))

seen = set()
unique = []

for word in words:
    if word not in seen:
        seen.add(word)
        unique.append(word)

print(unique)

Dictionary keys are unique and keep the insertion order, so dict.fromkeys removes duplicates in one line. The loop keeps an element only if it has not been seen before. Converting to a set would also remove duplicates, but it loses the order.

$ python unique_values.py
['sky', 'cup', 'wood', 'forest']
['sky', 'cup', 'wood', 'forest']

Filtering with a loop

The following example uses a plain for loop to filter a list.

custom_filter.py
vals = [-1, 2, 0, 11, 9, -3, -4, 3]
positive = []

for val in vals:
    if val > 0:
        positive.append(val)

print(positive)

We use a for loop and an if condition to collect all positive values. A loop is the clearest choice when the filtering logic needs several statements.

$ python custom_filter.py
[2, 11, 9, 3]

Lazy filtering

A generator expression filters lazily; it produces the elements one by one when they are requested and does not build a list.

lazy_filter.py
import sys

nums = range(1_000_000)

squares = [n * n for n in nums if n % 7 == 0]
lazy = (n * n for n in nums if n % 7 == 0)

print(sys.getsizeof(squares), sys.getsizeof(lazy))
print(sum(lazy))
print(sum(squares))

The list of squares of multiples of seven takes over a megabyte of memory, while the generator object takes a few hundred bytes. Both give the same sum. Like the filter iterator, a generator can be consumed only once.

$ python lazy_filter.py
1283160 216
47619404762214285
47619404762214285

Performance

We compare the speed of the three approaches.

timing.py
import timeit

setup = 'vals = list(range(100_000))'

tests = {
    'comprehension': '[e for e in vals if e % 2]',
    'filter + lambda': 'list(filter(lambda e: e % 2, vals))',
    'for loop': '''
res = []
for e in vals:
    if e % 2:
        res.append(e)
''',
}

for name, stmt in tests.items():
    t = min(timeit.repeat(stmt, setup, number=20, repeat=5))
    print(f'{name:16} {t / 20 * 1000:.2f} ms')

We filter the odd numbers of a list of 100,000 integers and take the best of five measurements. The list comprehension is the fastest. The filter function with a lambda is slower, because it calls a Python function for each element. With a built-in predicate such as str.isdigit, there is no Python function call, and filter is usually faster than a comprehension.

$ python timing.py
comprehension    2.24 ms
filter + lambda  3.35 ms
for loop         2.35 ms

Source

List comprehensions - Python documentation

In this article we have filtered lists in Python.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.