How to Filter Data in Python with Type Hints

A reusable filter_data helper uses optional predicates and numeric bounds with modern Python type hints.

Easy Python 3.10+ Aug 9, 2026 Testing & modern typing 12 views 0 copies

Python code

35 lines
Python 3.10+
from typing import Iterable, TypeVar, Callable, Any

T = TypeVar("T")

def filter_data(
    items: Iterable[T],
    predicate: Callable[[T], bool] | None = None,
    *,
    min_value: float | None = None,
    max_value: float | None = None,
) -> list[T]:
    """Filter items by predicate and/or numeric bounds."""
    result: list[T] = []
    for item in items:
        if predicate is not None and not predicate(item):
            continue
        if isinstance(item, (int, float)):
            if min_value is not None and item < min_value:
                continue
            if max_value is not None and item > max_value:
                continue
        result.append(item)
    return result

if __name__ == "__main__":
    numbers = [1, 5, 8, 12, 20, 3, 7]
    
    even_numbers = filter_data(numbers, predicate=lambda x: x % 2 == 0)
    print(f"Even numbers: {even_numbers}")
    
    bounded = filter_data(numbers, min_value=5, max_value=15)
    print(f"Between 5 and 15: {bounded}")
    
    combined = filter_data(numbers, predicate=lambda x: x > 4, max_value=10)
    print(f"Greater than 4 and at most 10: {combined}")

Output

stdout
Even numbers: [8, 12, 20]
Between 5 and 15: [5, 8, 12, 7]
Greater than 4 and at most 10: [5, 8, 7]

How it works

The filter_data function accepts an iterable and an optional predicate callable. It uses TypeVar to keep the return type consistent with the input element type. Numeric bounds are checked only for int and float items, leaving other types unaffected. The * in the signature makes min_value and max_value keyword-only, preventing accidental positional misuse. The function returns a new list, leaving the original data unchanged.

Common mistakes

  • Checking numeric bounds on non-numeric items without an isinstance guard
  • Forgetting the keyword-only marker `*` before min_value/max_value
  • Assuming the predicate is always provided; it defaults to None
  • Returning a generator instead of a list when a list is expected

Variations

  1. Use a list comprehension with conditionals for simpler one-off filtering.
  2. Leverage `functools.partial` or `lambda` wrappers for more complex reusable predicates.

Real-world use cases

  • Sanitizing API response data by keeping only numeric values within a valid range before further processing.
  • Building a generic utility in a data pipeline to filter rows by user-specified criteria without hardcoding each case.
  • Creating a validation helper in an ETL process to exclude outliers from sensor readings before analytics.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Testing & modern typing

Related tutorials and quizzes for this topic.