I currently use this strategy of a singleton object
when I cannot assign default arguments in a function's signature and/or None
already has meaning.
from typing import Optional
DEFAULT = object()
# `None` already has meaning.
def spam(ham: Optional[list[str]] = DEFAULT):
if ham is DEFAULT:
ham = ['prosciutto', 'jamon']
if ham is None:
print('Eggs?')
else:
print(str(len(ham)) + ' ham(s).')
However I get the following Error from mypy:
main.py:7: error: Incompatible default for argument "ham" (default has type "object", argument has type "Optional[List[str]]")
Found 1 error in 1 file (checked 1 source file)
ham
without getting errors in mypy? orDEFAULT = object()
?Something I like to do — which is only a slight variation on @Blckknght's answer — is to use a metaclass to give my sentinel class a nicer repr and make it always-falsey.
sentinel.py
from typing import Literal
class SentinelMeta(type):
def __repr__(cls) -> str:
return f'<{cls.__name__}>'
def __bool__(cls) -> Literal[False]:
return False
class Sentinel(metaclass=SentinelMeta): pass
main.py
from sentinel import Sentinel
class DEFAULT(Sentinel): pass
You use it in type hints exactly in the same way @Blckknght suggests:
def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT): ...
But you have the added advantages that your sentinel value is always falsey and has a nicer repr:
>>> DEFAULT
<DEFAULT>
>>> bool(DEFAULT)
False