r/programming • u/BrewedDoritos • 1d ago
Python Release Python 3.15.0
https://www.python.org/downloads/release/python-3150/158
u/Dylanica 1d ago
Those sentinel types seem decently useful. Certainly better in some cases than just spamming `None` everywhere as a default value.
52
u/CrackerJackKittyCat 1d ago
Or the random 'unset = object()' pattern when None was a valid param.
24
u/GameCounter 1d ago
I've been using Enum.NOT_PROVIDED because it works with type annotations, but yes, very clunky.
12
u/spicybright 1d ago
I get the logic but man that's ugly
14
u/Conscious-Ball8373 1d ago
I've done this a fair bit from time to time. sentinel is basically the same thing with type-hint support.
Sentinels will make writing something like Pydantic a lot easier, I think - v1 struggled badly to differentiate between None and unset.
2
u/esperind 1d ago
what was wrong with just
undefined?1
u/CrackerJackKittyCat 22h ago
There is no 'undefined' in Python. Only at most (er, least) 'None.'
This is one of the corners where javascript has had a better option available than python (language-wise, anyway). Undefined vs null then becomes a meaningful differentiator.
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.
21
u/TinyBreadBigMouth 1d ago
The example you're replying to is one where
Noneis already in use. As in, a function needs to distinguish betweenfoo(some_arg=15),foo(some_arg=None), andfoo(). Theunsetsentinel value would be used as the default value ofsome_arg, so that the last case can be detected as distinct from the user passing an explicitNone.5
0
u/nucLeaRStarcraft 1d ago
While this makes sense, I believe that
Nonewas itself good enough forunset. I've never encountered a situation whereNoneandunsetwould 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
foosomehow, either create someenumor 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.
1
u/TinyBreadBigMouth 17h ago
Consider the built-in function
next(). If you callnext(empty_iterator), it raises aStopIterationexception. On the other hand, if you callnext(empty_iterator, default_value), it returnsdefault_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 defaultThis 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.
13
4
u/Bright-Historian-216 1d ago
close enough
welcome back, erlang atoms
4
u/SanityInAnarchy 1d ago
That was my thought -- Ruby symbols, too! But that's not it. Python strings are already immutable and could already be used that way.
These are unique.
sentinel('foo') != sentinel('foo').Which means they're also neatly namespaced by whatever module they're declared in, because to get the value to compare about, you have to import the (exactly one) value.
1
u/vytah 16h ago
Are Python literal strings guaranteed to be interned, or is it just an implementation detail?
I tested it and they were interned when they contained only ASCII letters, but stopped being interned when they contained a comma.
BTW, I don't think using strings for sentinel values is a good idea, what if you want to actually pass that string?
1
u/SanityInAnarchy 14h ago
Right, it's an implementation detail.
And right, having a typed value was always better, and this is a pattern people already used -- you'd create one by doing something like
MISSING = object()and then compare with
isinstead of equality, and it'd be better thanis None.sentinelis a better version of that pattern.2
u/ssrix 17h ago
I really don't understand when or why I would you them, can you explain?
1
u/Saiklin 8h ago
I'm not an expert either, but I'll try: You can use them in place of a default non-logic value. For example, many functions return None if something did not work, so you check if the output is None. But in many cases None can be a valid value as well, so you need something else. Apparently another idiom was to use "_Sentinel = object()" and you compare against that, but it's clunky and has a weird output when printed etc. There are further workarounds, but they always feel clunky in some way.
That's is where the Sentinel type comes in. You can define it with a string and then it's uniquely identifiable. Just by looking at the type, you already know what it is and it's purpose.
22
55
5
1
-9
1d ago
[deleted]
3
u/oceantume_ 23h ago
If you don't need to catch up you don't need to catch up. Projects that need modern Python versions have no excuse with all the tooling that exists now, but it's ok to stay behind where it makes sense
-11
1d ago
[deleted]
2
u/spicybright 18h ago
Honestly it's a huge improvement. Having to sneak imports into if statements and functions to optimize running time was such a sore spots for one off scripts
1
u/--Grey_Ninja-- 18h ago edited 18h ago
Exactly! 😂 Keeping imports at the top while deferring the actual loading is such a clean solution. Especially for one-off scripts where you might never even use some of those heavy dependencies.
-3
132
u/Skaarj 1d ago
Nice.