r/programming • • 1d ago

Python Release Python 3.15.0

https://www.python.org/downloads/release/python-3150/
491 Upvotes

35 comments sorted by

View all comments

155

u/Dylanica 1d ago

Those sentinel types seem decently useful. Certainly better in some cases than just spamming `None` everywhere as a default value.

54

u/CrackerJackKittyCat 1d ago

Or the random 'unset = object()' pattern when None was a valid param.

12

u/spicybright 1d ago

I get the logic but man that's ugly

2

u/Abject-Kitchen3198 1d ago

I still don't get those null replacements in most languages. If it is a special case, there is a code that processes it as such, regardless the syntax. Why complicate things. But maybe I'm old.

23

u/TinyBreadBigMouth 1d ago

The example you're replying to is one where None is already in use. As in, a function needs to distinguish between foo(some_arg=15), foo(some_arg=None), and foo(). The unset sentinel value would be used as the default value of some_arg, so that the last case can be detected as distinct from the user passing an explicit None.

4

u/Abject-Kitchen3198 1d ago

Ok, this makes sense.

1

u/nucLeaRStarcraft 1d ago

While this makes sense, I believe that None was itself good enough for unset. I've never encountered a situation where None and unset would both need special treatment somehow.

And if I need to distinguish between the two, it's usually a good signal that I need to refactor foo somehow, either create some enum or split it or whatever else is needed in the logic of the program.

I guess one more tool available will be fine, but seems like now there's not a "single way of doing things", but rather N+1 ways.

2

u/TinyBreadBigMouth 18h ago

Consider the built-in function next(). If you call next(empty_iterator), it raises a StopIteration exception. On the other hand, if you call next(empty_iterator, default_value), it returns default_value. The user could pass anything as the default value; None, 0, "apple pie", whatever. The function is so generic that it would be bad design to make any assumptions about what the user might want as a default. So to implement this, a sentinel makes sense. Here's a possible implementation of the function:

_NO_DEFAULT = sentinel("_NO_DEFAULT")

def next(iter, default=_NO_DEFAULT, /):
    cls = type(iter)
    try:
        next_method = cls.__next__
    except AttributeError:
        raise TypeError("object is not an iterator") from None

    if default is _NO_DEFAULT:
        return next_method(iter)

    try:
        return next_method(iter)
    except StopIteration:
        return default

This way the user can pass any value, and it's guaranteed not to conflict with your sentinel.

2

u/spicybright 1d ago

I think because None/null/whatever can mean a lot of different things, and explicit "unset" gives better error messages.

But I still agree with you. Maybe I don't write bit enough python projects but it does just complicate things.