How to Build a Chainable Filter Helper in Python

A beginner-friendly dataclass helper that chains filters, uniqueness, and slicing on any sequence, returning a plain list at the end.

Easy Python 3.9+ Aug 9, 2026 Modern tooling 14 views 0 copies

Python code

42 lines
Python 3.9+
from dataclasses import dataclass
from typing import Callable, Iterator, Sequence, TypeVar

T = TypeVar("T")


@dataclass
class FilterAssistant:
    """Beginner-friendly helper to filter any collection."""

    data: Sequence[T]

    def where(self, predicate: Callable[[T], bool]) -> "FilterAssistant":
        return FilterAssistant([item for item in self.data if predicate(item)])

    def first_n(self, n: int) -> "FilterAssistant":
        return FilterAssistant(self.data[:n])

    def unique(self) -> "FilterAssistant":
        seen = set()
        result = []
        for item in self.data:
            if item not in seen:
                seen.add(item)
                result.append(item)
        return FilterAssistant(result)

    def apply(self) -> list:
        return list(self.data)


if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3, 7]
    result = (
        FilterAssistant(numbers)
        .where(lambda x: x % 2 == 0)
        .where(lambda x: x > 2)
        .unique()
        .first_n(3)
        .apply()
    )
    print(result)

Output

stdout
[4, 6, 8]

How it works

This code uses a @dataclass to store the current filtered sequence, and each method returns a new FilterAssistant instance, enabling chaining. The where method applies a predicate using a list comprehension, keeping only items that satisfy the condition. unique removes duplicates while preserving order by tracking seen items in a set. first_n slices the sequence to the first n elements. The final apply method returns a plain list, making the helper easy to use in any context.

Common mistakes

  • Forgetting that methods return a new FilterAssistant, not the original data
  • Not using type hints, which reduces readability and maintainability
  • Using a mutable default or modifying the original sequence unintentionally

Variations

  1. Use a generator expression in `where` to avoid creating an intermediate list
  2. Implement `__iter__` on the class to allow direct iteration instead of calling `apply()`

Real-world use cases

  • Building a reusable data-cleaning pipeline that filters rows by multiple conditions in a Jupyter notebook.
  • Creating a chainable query builder for in-memory collections in a CLI data analysis tool.
  • Teaching beginners how fluent interfaces work by chaining transformations on a small dataset.

Sponsored

Run this sample

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

Open editor

More from Modern tooling

Related tutorials and quizzes for this topic.