Skip to content

Instantly share code, notes, and snippets.

@markusand
Created September 23, 2026 08:27
Show Gist options
  • Select an option

  • Save markusand/d80c882b0ff72af0848af206c43dac0d to your computer and use it in GitHub Desktop.

Select an option

Save markusand/d80c882b0ff72af0848af206c43dac0d to your computer and use it in GitHub Desktop.
Predicate pattern example
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()
"""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