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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
List all Python tutorials.