On 14. 05. 21 10:55, Victor Stinner wrote:
Hi Tal,
Would it make sense to have an unique singleton for such sentinel, a built-in singleton like None or Ellipsis? I propose the name "Sentinel".
Sentinel would be similar to None, but the main property would be that "Sentinel is None" is false :-)
If you need your Sentinel to be different from one particular sentinel (None), you'll usually want it to be different from all the other ones as well.
A sentinel for an optional parameter shouldn't really be used at all outside of the function it's defined for. That's why it's usually defined as a private module-level variable.
Perhaps it would be beneficial to provide a common base class or factory, so we get a good repr. But I don't think another common value like None and Ellipsis would do much good.
The stdlib contains tons of sentinels:
- _collections_abc: __marker__
- cgitb.__UNDEF__
- configparser: _UNSET
- dataclasses: _HAS_DEFAULT_FACTORY, MISSING, KW_ONLY
- datetime.timezone._Omitted
- fnmatch.translate() STAR
- functools.lru_cache.sentinel (each @lru_cache creates its own sentinel object)
- functools._NOT_FOUND
- heapq: temporary sentinel in nsmallest() and nlargest()
- inspect._sentinel
- inspect._signature_fromstr() invalid
- plistlib._undefined
- runpy._ModifiedArgv0._sentinel
- sched: _sentinel
- traceback: _sentinel
There are different but similar use cases:
- Optional parameter: distinguish between func() and func(arg=value),
a sentinel is useful to distinguish func() from func(arg=None)
- Look into a data structure for a value and store the result in a
value, distinguish if 'result' variable was set ("result is not None" doesn't work since None is a value). Quick example: "missing = object(); tmsg = self._catalog.get(message, missing); if tmsg is missing: ..."
Special cases:
- dataclases._EMPTY_METADATA = types.MappingProxyType({})
- string._sentinel_dict = {}
- enum: _auto_null = object()
Victor
On Thu, May 13, 2021 at 7:40 PM Tal Einat taleinat@gmail.com wrote:
On Thu, May 13, 2021 at 7:44 PM Ethan Furman ethan@stoneleaf.us wrote:
Consider me complaining. ;-)
+1
An actual Sentinel class would be helpful:
>>> class Sentinel: ... def __init__(self, repr): ... self.repr = repr ... def __repr__(self): ... return self.repr ... >>> MISSING = Sentinel('MISSING') >>> MISSING MISSING >>> implicit = Sentinel('<implicit>') >>> implicit <implicit>
Here is my suggestion (also posted on the related bpo-44123), which is also simple, ensures a single instance is used, even considering multi-threading and pickling, and has a better repr:
class Sentinel: def __new__(cls, *args, **kwargs): raise TypeError(f'{cls.__qualname__} cannot be instantiated')
class MISSING(Sentinel): pass
- Tal
Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/URFRF634... Code of Conduct: http://python.org/psf/codeofconduct/