Python: Tell an Omitted Argument Apart From None
Your update function has to tell “keep the current address” apart from “clear it,” and a None default treats them the same. What does Python 3.15 offer for the missing argument?

- When None is a valid input, use a separate marker for an omitted argument.
- Create a Python 3.15 sentinel once and reuse it as both the default and the identity-check target.
- Two sentinels with the same name are separate objects; copying a sentinel and recreating it are different operations.
Give “leave unchanged” a separate value
An update function may need three states: omit an argument to retain the current setting, pass None to clear it, or pass a new value to replace it. With new=None as the default, the first two calls become indistinguishable. Python 3.15's built-in sentinel supplies a distinct default.
This illustrative function chooses the next notification address. It defines the input contract without sending any email:
MISSING = sentinel("MISSING")
def next_address(current, new=MISSING):
if new is MISSING:
return current
return new
assert next_address("[email protected]") == "[email protected]"
assert next_address("[email protected]", None) is None
assert next_address("[email protected]", "[email protected]") == "[email protected]" The name MISSING is a convention. What matters is that the default and the comparison refer to the same object. PEP 661 identifies valid None inputs as a reason to use a separate sentinel.
Check identity, not truthiness
new is MISSING asks whether the marker was supplied. if not new groups together values such as None, an empty string, zero and False. It answers a different question. The built-in sentinel itself is truthy.
Every call creates a unique object, even when the name is identical:
MISSING = sentinel("MISSING")
another = sentinel("MISSING")
assert another is not MISSING If several modules exchange the marker, define it once and import it. Recreating it by name does not recover the original identity.
The older object() idiom still separates the states
MISSING = object() can distinguish omission from None when the same object is reused. The new built-in adds a readable representation and retains identity through shallow and deep copying.
Those properties matter when markers travel inside copied data structures or appear in interfaces shared across modules. A small function that only checks a shared marker does not need to migrate just because a new type exists. Libraries supporting Python before 3.15 must also account for the built-in's availability.
Make a serialized marker discoverable
Pickling preserves a sentinel's identity when it can be retrieved by module and name. The straightforward arrangement is MISSING = sentinel("MISSING") at the top level of an importable module.
A sentinel created only in a function's local scope is not picklable. Copying guarantees do not remove that requirement. Define one shared marker first, then choose a discoverable home for it if serialization becomes part of the interface. None can then retain its application meaning while omission remains a separate choice.