The Python filter() function takes a test function and an iterable, and returns a lazy iterator with only the items for which the test returns a true value. We wrap the result in list() when we need a new list, and the original list stays unchanged.
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda n: n % 2 == 0, numbers)) # [2, 4, 6]
We walk through the syntax and return value of filter(), filtering numbers, strings and objects, filtering by another list, complex conditions, removing None and empty values, itertools.filterfalse() and the comparison with list comprehensions. The examples run on Python 3.14.
1. Syntax and Return Value
The function accepts two arguments. The first is a function that receives one item and returns something truthy or falsy, and the second is any iterable such as a list, tuple, set, string or generator.
filter(function, iterable) -> filter object (an iterator)
- function is called once per item. When it is None, filter() keeps the items that are truthy on their own.
- iterable is read one item at a time, so filter() also works on files and generators.
- Return value is a filter object, an iterator that produces the matching items in their original order. It does not return a list.
A filter object can be consumed only once. The second list() call in the next snippet returns an empty list, so we store the result in a list when we need it more than once.
result = filter(lambda n: n > 2, [1, 2, 3, 4])
kind = type(result).__name__ # 'filter'
first = list(result) # [3, 4]
second = list(result) # [], the iterator is used up
2. Filtering Examples
Each example passes a different kind of list to filter(), and the test function decides what stays.
2.1. Filter a List of Numbers
Filtering a list of numbers selects the elements that meet a numeric condition. The example keeps the even numbers, and the equivalent for loop shows what filter() does.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(filtered_numbers)
loop_result = []
for x in numbers:
if x % 2 == 0:
loop_result.append(x) # same result as filter()
[2, 4, 6, 8, 10]
2.2. Filter a List of Strings
Filtering strings selects the values that match a pattern. In this example, we keep the strings that contain the substring ‘an’. The word grape does not contain ‘an’, so it is not part of the result.
strings = ["apple", "banana", "orange", "grape", "kiwi"]
pattern = "an"
filtered_strings_list = list(filter(lambda x: pattern in x, strings))
print(filtered_strings_list)
['banana', 'orange']
For a case-insensitive match, we compare lowercase values with pattern in x.lower(). For patterns, a compiled regular expression supplies the test, because its search() method returns a match object or None.
import re
codes = ["tea-01", "milk", "jam-22", "bread-x"]
numbered = list(filter(re.compile(r"-\d+$").search, codes)) # ['tea-01', 'jam-22']
2.3. Filter a List of Objects
To filter a list of objects, the condition reads one or more attributes of each object. The example keeps the Person objects whose age is greater than 28.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
people = [Person("Alice", 30), Person("Bob", 25), Person("Charlie", 35)]
selected_people = list(filter(lambda p: p.age > 28, people))
for person in selected_people:
print(person.name, person.age)
Alice 30
Charlie 35
Dictionaries work the same way, with lambda p: p[“age”] > 28 as the test.
2.4. Filter a List of Lists
For nested lists, the test receives a whole inner list. We can keep rows by their length or content, or filter the items inside each row with a comprehension.
rows = [[1, 2], [3], [4, 5, 6]]
long_rows = list(filter(lambda r: len(r) > 1, rows)) # [[1, 2], [4, 5, 6]]
odd_items = [list(filter(lambda n: n % 2, r)) for r in rows] # [[1], [3], [5]]
3. Filter a List Based on Another List
To keep the elements of main_list that also appear in filter_list, the test function checks membership. Converting the second list to a set first makes each check fast, which matters when both lists are large.
main_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filter_list = [2, 4, 6, 8, 10]
allowed = set(filter_list)
kept = list(filter(lambda x: x in allowed, main_list)) # [2, 4, 6, 8, 10]
removed = list(filter(lambda x: x not in allowed, main_list)) # [1, 3, 5, 7, 9]
A membership test on a list scans the whole list every time, while a set lookup takes about the same time for any size. With 10,000 items in each list, the set version ran about 1,000 times faster in our test (0.4 ms against 465 ms).
4. Filter a List by Complex Condition
When a condition has several parts, we move it into a named function and pass that function to filter(). Named functions are easier to test and to read than a long lambda.
In this example, we keep the numbers that are greater than 5 and sit at an even index. The enumerate() function supplies the index together with each number.
def is_wanted(pair):
idx, num = pair
return num > 5 and idx % 2 == 0
numbers = [3, 8, 2, 10, 6, 4, 7, 9]
pairs = filter(is_wanted, enumerate(numbers))
filtered_numbers = [num for idx, num in pairs]
print(filtered_numbers)
[6, 7]
The numbers 8 and 10 are greater than 5 but sit at the odd indexes 1 and 3, so they are left out. The numbers 6 and 7 sit at the even indexes 4 and 6.
5. Handling None or Empty Values
Passing None as the function keeps only the truthy items. It removes None, empty strings, empty lists and False in one call.
data = ["tea", None, "", "milk", [], "sugar"]
clean = list(filter(None, data)) # ['tea', 'milk', 'sugar']
The value 0 is also falsy, so filter(None, …) drops zeros from a list of numbers. When zero is a valid value, we test for None explicitly.
readings = [0, None, 12, None, 7]
dropped_zero = list(filter(None, readings)) # [12, 7]
kept_zero = list(filter(lambda v: v is not None, readings)) # [0, 12, 7]
A custom function can combine both checks. The next function keeps even integers and skips None and empty strings first, so the % operator never sees a string.
def filter_condition(value):
return value is not None and value != "" and value % 2 == 0
data = [1, None, 3, None, "", 6, "", 8, 9, None]
filtered_data = list(filter(filter_condition, data)) # [6, 8]
6. Keep the Rejected Items with itertools.filterfalse()
The itertools.filterfalse() function is the opposite of filter(). It returns the items for which the test is false, which helps when we want both halves of a split.
from itertools import filterfalse
scores = [45, 82, 67, 30, 91]
passed = list(filter(lambda s: s >= 50, scores)) # [82, 67, 91]
failed = list(filterfalse(lambda s: s >= 50, scores)) # [45, 30]
7. filter() vs List Comprehension
A list comprehension with an if clause does the same job as filter(), and many developers prefer it when the test needs a lambda. The filter() function reads well when a named function already exists.
words = ["Tea", "", "milk", "", "Sugar"]
with_filter = list(filter(None, words)) # ['Tea', 'milk', 'Sugar']
with_comprehension = [w for w in words if w] # ['Tea', 'milk', 'Sugar']
capitals = list(filter(str.istitle, words)) # ['Tea', 'Sugar']
| Feature | filter() | List comprehension |
|---|---|---|
| Result | Lazy iterator | New list |
| Transform items as well | Needs map() | Yes, in the same expression |
| Best fit | Existing function such as str.isdigit | Inline condition |
| Memory for large input | Low, items are produced on demand | Whole list in memory (use a generator expression instead) |
More examples of the comprehension syntax are in Python list comprehension.
8. Real-World Example: Filtering Orders
An online shop exports orders as dictionaries, and the report needs only the paid orders above 50. A named test function keeps the rules in one place, and sum() consumes the filter object without building a list.
orders = [
{"id": 101, "status": "paid", "total": 120.0},
{"id": 102, "status": "pending", "total": 80.0},
{"id": 103, "status": "paid", "total": 35.5},
{"id": 104, "status": "paid", "total": 64.0},
]
def is_large_paid(order):
return order["status"] == "paid" and order["total"] > 50
large_paid_ids = [o["id"] for o in filter(is_large_paid, orders)] # [101, 104]
revenue = sum(o["total"] for o in filter(is_large_paid, orders)) # 184.0
9. filter() FAQs
A few practical questions remain about dictionaries, single matches, the original list and speed.
9.1. Does filter() change the original list?
No. It reads the list and produces the matching items, so the source list keeps all its elements. To change the list in place, we assign the result back to a full slice.
items = [3, 0, 5, 0]
items[:] = filter(None, items) # items is [3, 5]
9.2. How do we filter a dictionary?
We filter its items() and build a new dictionary from the pairs that pass the test.
stock = {"tea": 0, "milk": 4, "sugar": 2}
in_stock = dict(filter(lambda kv: kv[1] > 0, stock.items())) # {'milk': 4, 'sugar': 2}
9.3. How do we get only the first match?
The next() function reads the first item from the filter object and stops there, and its second argument is returned when nothing matches.
first_big = next(filter(lambda n: n > 10, [4, 12, 30]), None) # 12
no_match = next(filter(lambda n: n > 99, [4, 12, 30]), None) # None
9.4. Is filter() faster than a list comprehension?
With a built-in function such as str.isdigit or None, filter() is often a little faster. With a lambda, the comprehension is as fast or faster, because it avoids one function call per item. The difference matters only for very large lists.
10. Conclusion
The filter() function returns a one-pass iterator with the items that pass a test, and list() turns it into a new list. Passing None removes falsy values, a set speeds up filtering by another list, and filterfalse() returns the rejected items. When the test needs a lambda, a list comprehension is the more readable choice.
11. References
Happy Learning !!