pythonmypypython-typing

Type-hinting parameters with a sentinel value as the default


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)

Solution

  • 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