On 26.05.20 14:10, David Mertz wrote:

All of those uses, including those where you say otherwise, treat None as a sentinel. In the iter() case, the optional seconds argument is *called* 'sentinel'. Guido recently mentioned that he had forgotten the two argument form of iter(), which is indeed funny... But useful.

Maybe we have a different understanding of "sentinel" in this context. I understand it as an auxiliary object that is used to detect whether the user has supplied an argument for a parameter or not. So if the set of possible (meaningful) arguments is "A" then the sentinel must not be an element of A. So in cases where None has meaning as an argument it can't act as a sentinel. `iter` is probably implemented via varargs but if it was designed to take a `sentinel=` keyword parameter then you'd need a dedicated sentinel object since the user can supply *any* object as the (user-defined) sentinel, including None:

    >>> list(iter([1, 2, None, 4, 5].pop, None))
    [5, 4]

Well, ok functions.reduce() really does make it's own sentinel in order to show NONE as a "plain value". So I'll grant that one case is slightly helped by a hypothetical 'undef'.

The NumPy, deque, and lru_cache cases are all ones where None is a perfect sentinel and the hypothetical 'undef' syntax would have zero value.

For both `deque` and `lru_cache` None is a sensible argument so it can't act as a sentinel. It just happens that these two cases don't need to check if an argument was supplied or not, so they don't need a sentinel. For the Numpy cases, `np.sum` and `np.diff`, None does have a meaning from user perspective, so they need a dedicated sentinel (which is `np._NoValue`). If `keepdims` is not supplied, it won't be passed on to sub-classes; if it is set to None then the sub-class receives `keepdims=None` as well:

    >>> class Test(np.ndarray):
    ...     def sum(self, **kwargs):
    ...         return kwargs
    ...
    >>> a = Test(0)
    >>> np.sum(a)
    {'axis': None, 'out': None}
    >>> np.sum(a, keepdims=None)
    {'axis': None, 'out': None, 'keepdims': None}

For `np.diff`, if no argument is provided for `append` (or `prepend`) then nothing is appended (prepended), otherwise the supplied value is used (including None):

    >>> np.diff([1, 2])
    array([1])
    >>> np.diff([1, 2], append=None)
    TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'

For `np.concatenate` None is a meaningful argument to `axis` since it will flatten the arrays before concatenation.


I was wondering if anyone would mention Pandas, which is great, but in many ways and abuse of Pythonic programming. There None in an initializing collection (often) gets converted to NaN, both of which mean "missing", which is something different. This is kind of an abuse of both None and NaN... which they know, and introduced an experimental pd.NA for exactly that reason... Unfortunately, so far, actually using of.NA is cumbersome, but hopefully that gets better next version.
I wouldn't say it's an abuse, it's an interpretation of these values. Using NaN has the clear advantage that it fits into a float array so it's memory efficient.

Within actual Pandas and function parameters, None is always a sentinel.

On Tue, May 26, 2020, 4:48 AM Dominik Vilsmeier <dominik.vilsmeier@gmx.de> wrote:

On 26.05.20 06:03, David Mertz wrote:

On Mon, May 25, 2020, 11:56 PM Christopher Barker 
well, yes and no. this conversation was in the context of "None" works fine most of the time.

How many functions take None as a non-sentinel value?! How many of that tiny numbers do so only because they are poorly designed.

None already is an excellent sentinel. We really don't need others. In the rare case where we really need to distinguish None from "some other sentinel" we should create our own special one.

The only functions I can think of where None is appropriately non-sentinel are print(), id(), type(), and maybe a couple other oddball special ones.

Seriously, can you name a function from the standard library or another popular library where None doesn't have a sentinel role as a function argument (default or not)?

* From the builtins there is `iter` which accepts a sentinel as second argument (including None).
* `dataclasses.field` can receive `default=None` so it needs a sentinel.
* `functools.reduce` accepts None for its `initial` parameter (https://github.com/python/cpython/blob/3.8/Lib/functools.py#L232).
* There is also [`sched.scheduler.enterabs`](https://github.com/python/cpython/blob/v3.8.3/Lib/sched.py#L65) where `kwargs=None` will be passed on to the underlying `Event`.

For the following ones None could be a sentinel but it's still a valid (meaningful) argument (different from the default):

* `functools.lru_cache` -- `maxsize=None` means no bounds for the cache (default is 128).
* `collections.deque` -- `maxlen=None` means no bounds for the deque (though this is the default).

Other example functions from Numpy:

* [`numpy.concatenate`](https://numpy.org/doc/1.18/reference/generated/numpy.concatenate.html) -- here `axis=None` means to flatten the arrays before concatenation (the default is `axis=0`).
* Any function performing a reduction, e.g. [`np.sum`](https://numpy.org/doc/1.18/reference/generated/numpy.sum.html) -- here if `keepdims=` is provided (including None) then it will passed to the `sum` method of ndarray-sub-classes, otherwise not.
* [`np.diff`](https://numpy.org/doc/1.18/reference/generated/numpy.diff.html) supports prepending / appending values prior to the computation, including None (though that application is probably rare).

_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/U4U7X36COLQ776LRB4O6O4BEXDXFWHJK/
Code of Conduct: http://python.org/psf/codeofconduct/

_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/CITAZRZXLAUAHBFPOKMFQZEBJ34OWJBS/
Code of Conduct: http://python.org/psf/codeofconduct/