Python Finally Gets a Built-in Sentinel to End the None Confusion
Much Ado About None: Sentinel Values in Python
None in Python is overloaded: it signals missing keys, unset configs, and errors, making ambiguity common. Sentinel values solve this by being outside the domain of valid values. After PEP 661 was deferred, the Steering Council accepted it, and Python 3.15 introduces a built-in sentinel that type checkers can narrow. The author shares takeaways from his talks at PyConDE, EuroPython, and Swiss Python Summit, recommending built-in sentinels over object() and Enum hacks.
Sentinel values resolve this by being a guard outside the domain of valid values, so they can never collide with an actual value.
- pwdisswordfishq
So much trouble to avoid introducing the optional monad.
- lpghatguy
When I was writing a bunch of Lua, I was a big fan of having a None sentinel value (Lua uses 'nil' for representing absence) and it was useful for a lot of things similar to Pydantic's MISSING.
This distinction is something that I think is actually handy about JavaScript, and distinguishing between 'null' and 'undefined' is sometimes useful for similar reasons.
- RantyDave
"A simple cache returns None both for a missing key and for a key that maps to None."
No, it returns KeyError. Or at least it should.
- est
I just return a customized Exception type. Works about the same.
- throwaway2037
Here is the sample code from the article:
> MISSING = sentinel("MISSING")
> def update_email(user_id: int, email: str | None | MISSING = MISSING) -> None:
> if email is MISSING:
> return # nothing to update
> elif email is None:
> db.delete_email(user_id)
> else:
> db.set_email(user_id, email.lower()) # narrowed to str
I see this style frequently from long time Python programmers. The language is so dynamic that people begin to think this is a good way to program. Hot take: It is not. Why not two different functions called delete_email() and set_email()? That would be more clear.