Many functions take an optional argument and need to know whether the caller supplied it. The simplest way is to give the argument a default of None and check for that. This stops working when None is a value the caller might pass on purpose. Consider a lookup that reads a setting by name. A setting may legitimately be stored as None, so a result of None could mean the setting holds that value, or that the setting does not exist at all.
The usual solution is to create a unique object and compare against it by identity:
import copy
_MISSING = object()
def get_setting(settings, name, default=_MISSING):
if name in settings:
return settings[name]
if default is _MISSING:
raise KeyError(name)
return default
settings = {'theme': 'dark', 'proxy': None}
print(get_setting(settings, 'theme')) # dark
print(get_setting(settings, 'proxy')) # None
print(get_setting(settings, 'lang', 'en')) # en
print(repr(_MISSING)) # <object object at 0x...>
print(copy.deepcopy(_MISSING) is _MISSING) # False
The approach works. Asking for proxy returns the stored None rather than being mistaken for a missing key, and asking for lang returns the supplied default. Without a default, the same call raises KeyError.
It has three drawbacks: a meaningless representation, an identity that copy.deepcopy does not preserve, and no type that lets a checker tell this object apart from other values.
Since Python 3.15, the built-in sentinel creates objects designed for this purpose:
import copy
MISSING = sentinel('MISSING')
def get_setting(settings, name, default=MISSING):
if name in settings:
return settings[name]
if default is MISSING:
raise KeyError(name)
return default
settings = {'theme': 'dark', 'proxy': None}
print(get_setting(settings, 'theme')) # dark
print(get_setting(settings, 'proxy')) # None
print(get_setting(settings, 'lang', 'en')) # en
print(repr(MISSING)) # MISSING
print(copy.deepcopy(MISSING) is MISSING) # True
Each drawback is addressed: the object prints as its name, copy.deepcopy returns the same object, and the sentinel can be used directly in a type expression such as str | MISSING.
Leave a Comment
Cancel reply