A simple pythonic implementation of the Specification Pattern [1] for Python 3.7 or higher.
The central idea of Specification is to separate the statement of how to match a candidate, from the candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for validation and for building to order.
pip install simple-specificationfrom simple_specification import Specification
# hardcoded specification
class Odd(Specification):
def is_satisfied_by(self, candidate: int) -> bool:
return candidate % 2 != 0
# parametrized specification
class DivisibleBy(Specification):
def __init__(self, number: int):
self.number = number
def is_satisfied_by(self, candidate: int) -> bool:
return candidate % self.number == 0>>> odd = Odd()
>>> even = ~odd
>>> div3 = DivisibleBy(3)
>>> div5 = DivisibleBy(5)>>> odd.is_satisfied_by(1)
True
>>> odd.is_satisfied_by(2)
False
>>> even.is_satisfied_by(3)
False
>>> even.is_satisfied_by(4)
True
>>> div3.is_satisfied_by(9)
True
>>> div5.is_satisfied_by(12)
False>>> # AND
>>> (even & div3).is_satisfied_by(6)
True
>>> (even & div3).is_satisfied_by(9)
False
>>> (even & div3 & div5).is_satisfied_by(30)
True
>>> (even & div3 & div5).is_satisfied_by(36)
False
>>>
>>> # OR
>>> (even | div3).is_satisfied_by(10)
True
>>> (even | div3).is_satisfied_by(15)
True
>>> (even | div3).is_satisfied_by(5)
FalseWarning
Always surround the negation with parenthesis
>>> (~odd).is_satisfied_by(2)
True
>>> (~div5).is_satisfied_by(25)
False
>>> (~div5).is_satisfied_by(11)
True
>>> (~(odd | div5)).is_satisfied_by(6)
True
>>> (~(odd | div5)).is_satisfied_by(7)
False>>> all_spec = Specification.all([~even, div3, div5])
>>> all_spec.is_satisfied_by(15)
True
>>> all_spec.is_satisfied_by(30)
False
>>> all_spec.is_satisfied_by(25)
False
>>> all_spec.is_satisfied_by(6)
False>>> div15 = DivisibleBy(3) & DivisibleBy(5)
>>> any_spec = Specification.any([div15, even])
>>> any_spec.is_satisfied_by(2)
True
>>> any_spec.is_satisfied_by(15)
True
>>> any_spec.is_satisfied_by(30)
True
>>> any_spec.is_satisfied_by(3)
False
>>> any_spec.is_satisfied_by(5)
False| [1] | Specifications, by Eric Evans and Martin Fowler. |