sentinels.sentinel

Sentinel values of different flavors. Can be used with functions or classes.

Some Use Casses

Could be used like an Enum.

from pythonic_fp.sentinels.sentinel import Sentinel

def calculate_something(n: int, x: float) -> tuple[Sentinel[int], float]:
    if n <= 0:
        return (Sentinel(0), x)
    return (Sentinel(n), x/n)

def process_result(pair: tuple[Sentinel[int], float]) -> float:
    if pair[0] is Sentinel(0):
        return 0.0
    return pair[1]

result = process_result(calculate_something(213, 15234.541))

Can be also be used as a private implementation detail for a class. Here is an example of an class that can take an “optional” value yet still be able to store None as a value.

from typing import ClassVar, Final
class my_class:

    _sentinel: Final[ClassVar[Sentinel[str]]] = Sentinel('_my_class_secret_str')

    def __init__(self, value: float | None | Sentinel[str]) -> None:
        if value is my_sentinel:
            self.value = 42.0
        else:
            self.value = value

    def get_value(self) -> float | None:
        return self.value

Note

Threadsafe.

Note

Can be compared using == and !=. A Sentinel value always equals itself and never equals anything else, especially other sentinel values.

To ensure that reference equality is used put the known sentinel value first.

Tip

  • don’t export when using as a hidden implementation detail.

  • does not clash with end user code

    • which may use None or () as their “sentinel” values.

final class pythonic_fp.sentinels.sentinel.Sentinel(flavor: H)