The filter(function, iterable) built-in function extracts elements from an iterable for which a predicate function evaluates to True.
Lazy Stream Filtering: In Python 3, filter() returns a memory-efficient iterator that computes items on demand in O(1) auxiliary space without creating intermediate lists. It accepts named functions, inline lambda expressions, or None to remove all falsy values. To transform elements rather than filter them, see map(), or reverse sequence ordering with reversed().
The filter() function accepts a test function (called a predicate) and an iterable collection:
# Formal syntax of built-in filter
filter_iterator = filter(function, iterable)
| Parameter | Type Accepted | Behavioral Details |
|---|---|---|
function |
Callable or None |
Function returning a boolean. If None, evaluates truthiness directly. |
iterable |
Iterable[T] |
Any Python iterable such as a list, tuple, string, set, dictionary view, or generator object. |
| Return Value | filter object |
A lazy generator-like iterator yielding matching elements on demand. |
Define a dedicated function that returns True for matching items and pass its reference to filter():
# 1. Using a user-defined predicate function
def is_even(num: int) -> bool:
return num % 2 == 0
numbers = [5, 82, 40, 37, 19, 94]
even_filter = filter(is_even, numbers)
print("Type of filter:", type(even_filter))
print("Filtered even numbers:", list(even_filter))
Output:
Type of filter:
Filtered even numbers: [82, 40, 94]
Passing even_filter to type() confirms it returns a lazy iterator rather than an eager list.
Because strings in Python are iterable sequences of characters, filter() operates seamlessly across textual data. You can filter individual characters using built-in string methods like islower() or inline lambda expressions:
# 1. Filtering string characters with a named function
def is_lowercase(char: str) -> bool:
return char.islower()
site_name = "Plus2Net Portal"
lower_filter = filter(is_lowercase, site_name)
print("Extracted lowercase characters:", list(lower_filter))
# 2. Filtering strings with an inline lambda
vowels = filter(lambda c: c.lower() in "aeiou", site_name)
print("Extracted vowels:", "".join(vowels))
Output:
Extracted lowercase characters: ['l', 'u', 's', 'e', 't', 'o', 'r', 't', 'a', 'l']
Extracted vowels: ueoea
When the first argument to filter() is None, Python applies an identity check: any element that evaluates to False in a boolean context (such as 0, "", None, [], or False) is stripped away automatically:
# 1. Filtering falsy elements with filter(None, iterable)
mixed_records = [0, "", None, "Python", [], '0', {}, False, 42, 0.0, "Active"]
truthy_records = filter(None, mixed_records)
print("Truthy values retained:", list(truthy_records))
# 2. Practical data cleaning: strip empty strings from user tags
raw_tags = ["python", "", "web", " ", "backend", None, "data"]
clean_tags = list(filter(None, [t.strip() if isinstance(t, str) else None for t in raw_tags]))
print("Sanitized tags:", clean_tags)
Output:
Truthy values retained: ['Python', '0', 42, 'Active']
Sanitized tags: ['python', 'web', 'backend', 'data']
filter(None, data) runs in compiled C without Python bytecode interpreter overhead, making it faster than equivalent list comprehensions when stripping falsy values from large datasets.
Both filter() and list comprehensions with conditions extract matching items. However, their memory profiles differ fundamentally:
| Criterion | filter(predicate, iterable) |
[x for x in iterable if condition] |
|---|---|---|
| Evaluation Timing | Lazy (computed on-demand during iteration) | Eager (materialized immediately in RAM) |
| Memory Footprint | Constant O(1) auxiliary pointer memory (48–56 bytes) | O(K) memory proportional to matching elements |
| Traversal Pass | Single-use (exhausts upon complete iteration) | Reusable indexable sequence |
| Best Used For | Pre-existing predicate callables, streaming large files | Inline arithmetic transformations, multi-pass workflows |
import sys
data_stream = range(1_000_000)
# filter() operates in O(1) auxiliary space (lazy evaluation)
lazy_filter = filter(lambda x: x % 100 == 0, data_stream)
# List comprehension operates in O(N) auxiliary space (eager materialization)
eager_list = [x for x in data_stream if x % 100 == 0]
print("Memory of filter iterator:", sys.getsizeof(lazy_filter), "bytes")
print("Memory of materialized list:", sys.getsizeof(eager_list), "bytes")
print("First three items via next():", [next(lazy_filter), next(lazy_filter), next(lazy_filter)])
Output:
Memory of filter iterator: 48 bytes
Memory of materialized list: 85176 bytes
First three items via next(): [0, 100, 200]
Below are common real-world patterns demonstrating filter() in API payload sanitization and server log ingestion:
# Recipe A: Validating user records
users = [
{"id": 101, "name": "alice", "active": True},
{"id": 102, "name": "", "active": False},
{"id": 103, "name": "charlie", "active": True}
]
valid_users = filter(lambda u: u["active"] and bool(u["name"]), users)
print("Valid users:", [u["name"] for u in valid_users])
# Recipe B: Streaming log error detection
logs = [
"09:01:15 WARN Memory high",
"09:02:40 ERROR DB connection failed",
"09:03:10 INFO Ping OK",
"09:04:05 ERROR Audit failed"
]
critical = filter(lambda line: "ERROR" in line, logs)
print("Errors caught:", list(critical))
Output:
Valid users: ['alice', 'charlie']
Errors caught: ['09:02:40 ERROR DB connection failed', '09:04:05 ERROR Audit failed']
list() a second time returns an empty list []. Materialize to a list if you need multi-pass access.filter(is_even(), numbers) invokes the function prematurely instead of passing the callable reference filter(is_even, numbers).(f(x) for x in data if pred(x)) are often preferred for readability.# Pitfall: Iterator exhaustion
numbers = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, numbers)
print("First pass:", list(evens))
print("Second pass (exhausted):", list(evens)) # Empty!
# Correct multi-pass approach: materialize to a list if needed
evens_list = list(filter(lambda x: x % 2 == 0, numbers))
print("Reusable list:", evens_list)
Output:
First pass: [2, 4, 6]
Second pass (exhausted): []
Reusable list: [2, 4, 6]
Practice stream processing, falsy data cleaning, and memory profiling in our companion Jupyter Notebook:
Explore interactive code cells, benchmark lazy filter memory against list comprehensions, build custom predicates, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify filter() mechanics:
def test_filter_mechanics():
# 1. Numerical filtering
assert list(filter(lambda x: x > 0, [-2, -1, 0, 1, 2])) == [1, 2]
# 2. filter(None) truthy filtering
assert list(filter(None, [0, 1, False, True, "", "hello", None])) == [1, True, "hello"]
# 3. String character filtering
assert "".join(filter(str.isalpha, "Py3.10!")) == "Py"
# 4. Iterator single-pass exhaustion
it = filter(lambda x: x % 2 == 0, [2, 4])
assert list(it) == [2, 4]
assert list(it) == []
print("All filter() unit assertions passed! 100% verified.")
test_filter_mechanics()
Output:
All filter() unit assertions passed! 100% verified.
Applies a function to all elements in an iterable, returning a lazy transformation iterator.
Returns a reverse iterator over sequence elements without creating a reversed copy.
Generates and advances forward through iterator sequences.
Provides a concise, eager syntax to create and filter lists in a single readable line.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.