Created
September 23, 2026 08:27
-
-
Save markusand/d80c882b0ff72af0848af206c43dac0d to your computer and use it in GitHub Desktop.
Predicate pattern example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from typing import Literal | |
| from dataclasses import dataclass | |
| from predicates import predicate, NOT, AND | |
| Gender = Literal['F', 'M'] | |
| @dataclass | |
| class User: | |
| name: str | |
| age: int | |
| gender: Gender | |
| @predicate | |
| def is_adult(user: User) -> bool: | |
| return user.age >= 18 | |
| @predicate | |
| def is_male(user: User) -> bool: | |
| return user.gender == 'M' | |
| def main(): | |
| john = User(name="John", age=20, gender='M') | |
| jane = User(name="Jane", age=19, gender='F') | |
| # is_adult_female = is_adult & ~is_male # pylint: disable=invalid-unary-operand-type | |
| is_adult_female = AND(is_adult, NOT(is_male)) | |
| # Use predicates | |
| print(f"{john.name} is adult?: {is_adult(john)}") | |
| print(f"{jane.name} is adult?: {is_adult(jane)}") | |
| print(f"{john.name} is adult female?: {is_adult_female(john)}") | |
| print(f"{jane.name} is adult female?: {is_adult_female(jane)}") | |
| if __name__ == '__main__': | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """Composable boolean predicates with operator support""" | |
| from __future__ import annotations | |
| from typing import Callable | |
| from functools import wraps | |
| class Predicate[T]: | |
| """A composable boolean predicate""" | |
| def __init__(self, fn: Callable[[T], bool]): | |
| self._fn = fn | |
| def __call__(self, obj: T) -> bool: | |
| return self._fn(obj) | |
| def __and__(self, other: Predicate[T]) -> Predicate[T]: | |
| return Predicate(lambda obj: self(obj) and other(obj)) | |
| def __or__(self, other: Predicate[T]) -> Predicate[T]: | |
| return Predicate(lambda obj: self(obj) or other(obj)) | |
| def __invert__(self) -> Predicate[T]: | |
| return Predicate(lambda obj: not self(obj)) | |
| def predicate[T](fn: Callable[[T], bool]) -> Predicate[T]: | |
| """Decorator that turns a plain function into a Predicate""" | |
| @wraps(fn) | |
| def wrapper(obj: T) -> bool: | |
| return fn(obj) | |
| return Predicate[T](wrapper) | |
| def AND[T](*predicates: Predicate[T]) -> Predicate[T]: # pylint: disable = invalid-name | |
| """Combine predicates with AND logic - all must be true.""" | |
| return Predicate(lambda obj: all(pred(obj) for pred in predicates)) | |
| def OR[T](*predicates: Predicate[T]) -> Predicate[T]: # pylint: disable = invalid-name | |
| """Combine predicates with OR logic - at least one must be true.""" | |
| return Predicate(lambda obj: any(pred(obj) for pred in predicates)) | |
| def NOT[T](predicate: Predicate[T]) -> Predicate[T]: # pylint: disable = invalid-name | |
| """Negate a predicate.""" | |
| return ~predicate |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment