Standardizing sentinel values finally replaces fragile `object()` hacks with a robust, type-safe solution for distinguishing missing data. It’s a subtle but essential evolution that brings much-needed clarity and professional rigor to Pythonic API design.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
There's a NEW built-in function coming in Python 3.15Added:
How's it going everyone? In today's video we're going to learn about PEP 661, which brings us another new feature in Python 3.15. PEP 661 introduces a new way of creating sentinel values in Python. Now, you might be asking, what is a sentinel value and where would you use one? Well, if your function needs to distinguish between two states such as a value that is missing versus a value that is explicitly none, you'll need to use some sort of placeholder for that missing value, which is often referred to as a sentinel value. Up until now, if we wanted to create a sentinel value, we just use either minus one, none, or some other placeholder we created such as missing. We could then use it in our function like this. Here we have a value which we will set to missing as the default. Now, if the value is equal to missing, we will print that there is no If the value is none, we will print that we are skipping the search. In every other case, we will search for that value. All a sentinel value does is signal the end of a data sequence or the absence of a value. In most cases, we could get away with using none, but there will be certain circumstances where none will not be appropriate and you'll want to use a more unique sentinel object to distinguish there being no value from there being a value of none. And to demonstrate that, I'm going to use another example which uses missing. Except this time, we're going to create a function called inspect type. Obviously, if we set this to none, that will cause an issue because none is a valid type. So, we want a more appropriate type that represents no value and in this example, missing is perfect. Now, if the object is or is equal to missing, we will ask the user to please insert a value. Else, we will print the type of the object. If we used is none here, we would be missing out on the none type. So, below we can run this with an integer and the none type. And as an output, we will get the integer type back and the none type back. If we insert no value, it will use the sentinel value. And you'll also see sentinel values being used in built-in methods such as string.find. Here, when we try to search for James, it's not going to be able to find James, so it's going to return the sentinel value of -1. Now, before we jump into PEP 661, I just like to thank Zed for sponsoring today's video. Zed is a lightning-fast code editor written in Rust and I've been using it to do all my coding for quite a while now. If you feel like trying out Zed, I've left a link in the description box down below where you can download it for free.
Anyway, let's get back to the video.
Right now, as of Python 3.15 alpha 6, the new built-in sentinel constructor has not been introduced into the language just yet. So, I won't be able to show you any live examples. Instead, I'm going to go over the PEP that explains why this made it to the language. Starting with the motivation, in May 2021, a question was brought up on the Python dev mailing list about how to better implement a sentinel value for traceback.print_exception.
The existing implementation used the following common idiom. And as you can see here, it's exactly how we created our missing value from earlier. However, this object has an uninformative and overly verbose representation, causing the function signature to be overly long and hard to read, which makes sense because when you try to represent an object, you get the memory address of that object. And that's not really something anyone can understand or at least not at a first glance.
Additionally, two other drawbacks of many existing sentinels were brought up in the discussion. Number one, some do not have a distinct type, hence it is impossible to define clear type signatures for functions with such sentinels as defaults. Number two, they behave unexpectedly after being copied due to a separate instance being created and thus comparisons using is failing.
Some common sentinel idioms have similar problems after being pickled and unpickled. And because of all of this, they decided that introducing a sentinel built-in function made sense. Moving on to the rationale, the criteria guiding the chosen implementation were number one, the sentinel objects should behave as expected by a sentinel object. When compared using the is operator, it should always be considered identical to itself but never to any other object.
Number two, creating a sentinel object should be a simple, straightforward, one-liner. Number three, it should be simple to define as many distinct sentinel values as needed. Number four, the sentinel objects should have a clear and short representation. Number five, it should be possible to use clear type signatures for sentinels. Number six, the sentinel objects should behave correctly after copying and sentinels should have predictable behavior when pickled and unpickled. Number seven, such sentinels should work when using CPython and PyPy3 and ideally also with other implementations of Python. And finally, number eight, as simple and straightforward as possible in implementation and especially in use.
Avoid this becoming one more special thing to learn when learning Python. It should be easy to find and use when needed and obvious enough when reading code that one would normally not feel a need to look up its documentation. Now, moving on to the section we've all been waiting for, the specification. What it's going to look like. So, here we have a new built-in callable named sentinel, which will soon be added. Here we can use the name of the sentinel object followed by the sentinel constructor. Sentinel takes a single positional-only argument such as name, which must be a string. Passing a non-string raises a type error. The name is used as the sentinel's name and representation. And sentinel objects have two attributes. Name is the sentinel's name and the module is the name of the module where sentinel was called. Sentinels cannot be subclassed.
Each call to sentinel returns a new sentinel object. If a sentinel is needed in more than one place, it should be assigned to a variable and that same object should be reused explicitly. Just as with the common missing equals object idiom. And as you can see here, we have a simple example of how it can be used, which is very similar to what we did in the code editor. So, instead of typing _missing = object, we just type in sentinel missing. And we could change that to missing, which is a slight improvement. Then, checking if a value is such a sentinel should be done using the is operator, just like we do with none. Equality checks using is equals to will also work as expected, returning true only when the object is compared with itself. Identity checks such as if value is missing should usually be used rather than boolean checks such as if value or if not value. Then we should note that sentinel objects are truthy and that creating a copy of a sentinel object will return the same object. Then they add some information on pickling, which we will skip for now. And moving on to the representation, the representation of a sentinel object is the name passed to sentinel. No implicit module qualification is added. If a qualified representation is desired, the qualified name should be passed explicitly. So, as you can see, the representation of a sentinel value is much easier to read than an object's memory address. Moving on, if you are a typing freak such as myself, you're going to want to know how to type this.
And the proposed way to do that is by using the name that you use for the sentinel value which you are creating, just like we do with the none type. As you can see down here, they create an optional value using the sentinel value, which is very straightforward. In terms of backwards compatibility, adding a new built-in means that code which currently relies on the bare name sentinel raising a name error will instead see the new built-in. This is the usual compatibility consideration for new built-ins. Existing local, global, and imported names called sentinel are unaffected. Code that already uses the new name sentinel will have to be adapted to use the new built-in and may receive new linter errors from linters that warn about collisions with built-in names. And luckily, this brings no security implications. Imagine if a sentinel value made Python vulnerable. I would both laugh and cry. And finally, we have a few rejected ideas, such as what we've been using currently, which suffers from all of the drawbacks that we just covered. Then there's adding a single new sentinel value such as missing or sentinel. And the reason this was rejected is because such a value could be used for various things in various places. One could not always be confident that it would never be a valid value in some use cases. On the other hand, a dedicated and distinct sentinel value can be used with confidence without needing to consider potential edge cases. Additionally, it is useful to be able to provide a meaningful name and representation for a sentinel value specific to the context where it is used. And finally, and the most important reason, this was a very unpopular option in the poll with only 12% of votes voting for it. And I'm kidding, of course, that's not the most important reason, but it is quite important. Then there are other rejected ideas such as using the existing ellipsis sentinel value, using a single-valued enum, a sentinel class decorator, class objects, and defining a recommended standard idiom without supplying an implementation. And there are actually a lot more, but I will just leave a link to this PEP in the description box down below so So can read it and explore it in case you're curious. But there's a lot of good stuff in here explaining why we need the sentinel value. Now, is this going to be a game-changer? Not really. It's just another very convenient and subtle change. Some devs might not even know this exists until Python 3.18, which might be perfectly fine. I mean, if we've made it all this way doing something silly such as creating an object each time we wanted to use a sentinel value, then there's a good chance we'll be able to continue doing this. But yeah, that just about covers everything I wanted to go over in today's video. Do let me know what you think about this upcoming Python feature in the comment section down below and what features you'd like to see in future Python versions. Otherwise, with all that being said, thanks for watching and I'll see you in the next video.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
Re: 🗣️📍theprophedu📍2026 GST 103 CLASS (E-EXAM REVISION)
theprophedu
636 views•2026-06-04
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Instagram accounts got PWNed
EricParker
13K views•2026-06-03











