Turn a binary predicate into a reusable filter
Also published in our Blogger archive.
A binary predicate accepts two operands, while filter() calls its predicate with one item at a time. functools.partial bridges that difference by fixing one argument. The expression partial(le, 18) creates a callable that receives score and evaluates le(18, score), which is the same comparison as 18 <= score.
The example applies that reusable predicate to the concrete score list [12, 18, 19, 7, 22]. filter() lazily selects scores meeting the cutoff; list() realizes the result as [18, 19, 22]. The first assertion checks the inclusive boundary, and the second checks the filtered output. Naming the partial object documents the business rule and means the same predicate can be passed to another iterable without rewriting a lambda.
Operand order is the easy part to get wrong. operator.le(a, b) represents a <= b, so binding 18 as its first argument means “at least 18.” Before Python 3.14, partial fills ordinary positional arguments from the left, so binding the second operand needs a wrapper. In Python 3.14 and later, functools.Placeholder permits an unfilled positional slot: partial(le, Placeholder, 18) would instead test whether an incoming value is at most 18. This pattern is appropriate only when the predicate’s fixed configuration is clear. For validation with multiple rules or helpful error messages, an ordinary named function can be easier to maintain.
partial() and the Python 3.14 Placeholder support are documented in the official functools reference. operator.le() is defined in the operator module reference.
AI assistance disclosure: this article was drafted with AI assistance and uses a deterministic synthetic dataset.
from functools import partial
from operator import le
scores = [12, 18, 19, 7, 22]
is_adult_score = partial(le, 18)
eligible = list(filter(is_adult_score, scores))
assert is_adult_score(18)
assert eligible == [18, 19, 22]
print(eligible)
[18, 19, 22]