Move dataclasses.MISSING into functools for general use by Python developers

There is a tricky problem in Python where it is hard to tell if an optional keyword argument as actually passed into the function by the caller. For example, it’s hard for f to differentiate between case 1 and 2 below:

def f(x, override=None):
    if override is not None:  # Assume y was provided
        ...

f(0)  # Case 1: y not provided by user
f(0, None)  # Case 2: y explicitly set by user

For cases where you want to be very careful about this distinction, a common pattern is to use a private object instance that the caller would never use:

_missing = object()
f(x, override=_missing):
    if override is not _missing:
        ...

This is actually the same pattern used by dataclasses.MISSING internally, and would be nice if it was promoted for general use by Python programs. I think it would be good if this was simply moved to typing/types/functools (not sure which one would make sense) and the docs updated to say this utility is meant for general use. I guess it’s possible someone will do something like f(0, MISSING) and then be surprised that the value is ignored, but I think this is enough of an edge case not to worry about vs. having an elegant utility variable built into the standard library to solve this pattern, especially given that the underlying code is already there.

References:

I think this is covered very well by PEP 661: Sentinel Values which is currently deferred but might make it into 3.15.

8 Likes

Oh thanks, yes this is perfectly covered by that PEP :slight_smile:

Note that sharing a missing sentinel across independent modules is a terrible idea that will lead to surprise bugs. Any tightly coupled group of functions/classes/modules should share it, but not beyond that, otherwise you are in the exact same situation as you are with None.

14 Likes