There certainly is precedence in Python using textual operators when other languages use punctuation. Using and and or instead of && and || is an obvious example, but Python (Guido?) somewhat recently deciding to use
a?.b ?? c - I prefer the long form c if a is None or a.b is None else a.b
and
a ? b : c - I prefer the long form b if a else c
One of the things I like in Python is how readable it is, as opposed to how compact it is. I also am quite happy when Python doesn’t follow the crowd. I have used a ?? b in Swift, but the alternative is `a == nil ? b : a`
On the clear in preference to compact front, it is better to contain the nitty-gritty of parsing rather free-form input into a separate class or module so that the main logic remains visible and isn’t buried in None checks. Which further devalues a compact notion.
Of more value is the optimisation that in a ?? b a is only evaluated once. Therefore I can see the point in a ifnone b or similar. But perhaps there is a compiler optimisation that would eliminate the need for that also.
Would like Python to remain none computer scientist friendly.
A nitpick I’d have with ifnone is that it, at least to me, makes a ifnone b kind of read as “a if a is None, else b”, when what’s really happening is the opposite. ifnotnone would be clearer, but even as someone who generally prefers longer-but-clearer code and Python’s keywords over certain symbol-based operators, having three words mushed into one keyword feels a bit odd.
Honestly even as a strong supporter of this I admit that it will probably feel a bit out of place for Python at first, but personally the benefits in brevity outweigh that strangeness. The := operator is also kind of a strange thing to encounter at first as well, but I’ve ended up using it quite a lot and gotten well used to seeing it, I think it’s entirely plausible the same can happen for ??, ?., and ?[].
This is all about not mangling your assignment syntax yet still being safe.
Just add a few characters to your existing code. No restructuring.
Whenever I write c if a is None or a.b is None else a.b (or anything more complicated than this) I’m usually breaking it into multiple lines, which completely defeats the purpose of inlining it and blows up the assignment syntax anyway.
Natural primitive for dynamic languages like Python where duck typing is prevailant and you very very often needing to ask for forgiveness, not permission.
Unfortunately (for your “strong +1”), this is not what the proposal does.
Duck typing does not naturally lead to the propagation of None in any way - they are entirely orthogonal. The concept of dealing with “members that may be None” is more likely to occur with a strict interface model, where you can’t get away with implementing “just enough” of an interface like you can in a duck-typing model like Python’s.
The proposal you seem to prefer would be the one that treats ?. as “get the attribute if the object has it, otherwise None” - this PEP (nor 823) is not that proposal. This is the one where the result of an operation is None and you want to break out of the current expression, which is neither a feature of duck-typing nor a likely outcome.
This feels to me like it’s the biggest flaw with this PEP. In spite of the fact that the PEP clearly specifies the behaviour, and we’ve had multiple discussions about what is and isn’t included in the behaviour, we still see people regularly misunderstanding what the operations do.
Even if I were in favour of the proposal, the ease with which it can be misinterpreted[1] feels like it’s a major flaw, and one that simply isn’t going away as time goes on. The usual argument against “this is hard to understand” concerns is “people need to learn new features, and once they do, it’ll be fine”, which doesn’t seem to be happening here.
something that I routinely do when I see similar operators in other languages ↩︎
My reason for sponsoring both PEPs is that they replace common, verbose idioms with concise custom syntax.
Take a recent example,
c if a is None or a.b is None else a.b
Even with coloring, when you’re skimming code, this reads like
c if bla bla bla or bla bla bla else a.b
In the version using ‘?.’ and ‘??’, each name is used exactly once. In the if/else version, you have to carefully read the bla bla bla to confirm the correspondence of the variables and expressions to know what it does. This slows down reading, distracting the reader from comprehending the overall structure.
P.S. I don’t believe that Nathaniel wanted the interpretation that Steve assumed he wanted. His text is exactly the a?.b ?? c that Steve wanted earlier (and quoted at the top here).
Yep, this is a very effective way to show the improvement that the syntax achieves, and I agree with it. (I only disagree with needing the None-check in the first place, but once you have it, the ?. and ?? syntax is an improvement - my fear is that the existence of the syntax will increase the prevalence of API designs that need None-checks generally, which is why I withdrew my support)
I believe you’re right, but I remember every previous discusson (and at least one podcast…) on this topic quickly hitting “it does None checks which is good because duck-typing”. Conflating the two leads to the confusion that Paul mentioned, as happened multiple times last time.
On this topic, which I’ve seen come up a number of times (more particularly on the discussion for 823):
While I can understand where you’re coming from, the reason why I would find so much use in both PEPs 823 and 824 is that there are already lots of APIs (maybe not so much in pure Python code, I’m particularly referring to things like external web APIs which is something many Python projects including mine are dealing with) that end up requiring these kinds of checks - the way I see it this pattern is going to exist either way, so we might as well have an easier way to deal with it.
What you’re observing in this thread is multiple different people repeatedly getting the interpretation wrong. But there’s not much opportunity for learning this new feature by trying to use it – all we have is the PEP text. (And a demo sandbox, but I suspect very few people tried that before posting here.)
I also believe that your argument “people misunderstand it” is stronger in the PEP-823 thread (a?.b) than here (a ?? b). Once you learn about ?? it’s not hard to remember what it does: it does the same as or, which is already in widespread (ab)use for this idiom), except it checks whether the left operand is None instead of falsy. The None check is typically what people want, but often we are too lazy to write the whole if x is None thing.
For me, the strongest use cases for ?? and ??= are for function arguments whose type is T | None where None implies “use some other specific default”. For example, a function argument that should be a list shouldn’t be written as arg: list[T] = [].[1] But we can write it as arg: list[T] | None and then at the top of the function write arg ??= [].
The nice thing is that in this case a type checker will infer arg’s type as list[T] in the rest of the function body, so you don’t have to worry about shutting the type checker up about the None case, even though the argument has | None in its type.
And if you don’t use type checking, it still solves a real problem – mutable defaults are still evil so you still need to include
if arg is None:
arg = []
Which I consider less readable than arg ??= [] because on first skim it reads as
if blah blah blah:
blah = []
and now you have to read more carefully to confirm that it’s a None check and an assignment to the variable that’s None, before you can recognize the idiom for “use default if None”.
The use case for ?? is actually ever so slightly weaker – its’ most useful when arg is used only once in the function body (maybe being passed to another function). But it would be too weird to introduce ??= without ??.
I used AI to find the most contentious remaining issues in this thread, so I can try to address them all in one omnibus post (without quoting from individual messages – these are issues that were brought up repeatedly). In decreasing order of importance it listed:
Whether PEP 824 is worthwhile independently of PEP 823
Whether ??= earns its own syntax
Why new syntax is justified at all
Why ?? is preferable to a keyword spelling
Whether the operator encourages excessive use of None
Exact None semantics
The relationship between the two PEPs7.
Mixing with and and or
Going over the list:
1. Whether PEP 824 is worthwhile independently of PEP 823
I’m combining this with 7. The relationship between the two PEPs.
Perhaps PEP 505 shouldn’t have been split, but it’s a bit late to coalesce both new PEPs back into a single one. Ideally, both would be accepted, as they have some synergy. But either one by itself adds a significant new feature that can stand on its own (and opposition to 823 seems somewhat stronger than to 824). In my last post I pointed out the use case I expect to be using this most for, and that most definitely stands on its own. And note that, for example, C# introduced ?? in 2005 but ?. a declade later.
2. Whether ??= earns its own syntax
In my last post I mentioned that ??= is a stronger feature than ?? so I do think so.
3. Why new syntax is justified at all
My main argument (for both PEPs) is that the more traditional spelling using e.g. b if a is None else a reads as b if bla bla bla else a, which requires careful reading to confirm that bla bla bla actually checks whether a is None and not whether some other variable is None.
4. Why ?? is preferable to a keyword spelling
It’s the spelling in every other language that has this feature. While I’ve sometimes chosen to use keywords instead of conventional operators (last in 2006 for the if-expression) I used existing simple English words, not made-up neologisms like ifelse. And I’m sorry, Barry, but I find otherwise too long to type, and also it’s rather nonspecific about what it does. ifnone is more specific, but still asks the question “if what is None?”. In any case there wasn’t much support for either of these alternatives.
5. Whether the operator encourages excessive use of None
Steve Dower and Mike H have brought this up. Honestly I don’t yet understand their arguments, and have asked for a clarification. My current position is that optional components occur all the time in real life data and we must be able to deal with them efficiently.
6. Exact None semantics
This issue asks about configurability of the specific check that’s being executed to test whether to take the second branch or not. There hasn’t been much support for this proposal. It slows down the common case significantly – we’d need a new bytecode instruction that checks for None. This is cheap; but if the value is notNone, it has to do an expensive method lookup to find the overridable dunder that implements the check, doing a lot of unnecessary work in the common case where no such dunder is defined. The cost of this would lead people to try to avoid the operator in favor of less precise approaches (notable going back to or).
7. The relationship between the two PEPs
Answered above with (1).
8. Mixing with and and or
This is the question about operator precedence. The PEP puts it above (grouping less tightly than) or. For example, a ?? b or c is read as a ?? (b or c), which makes some sense; also, a or b ?? c reads as (a or b) ?? c which I guess also makes sense.
The PEP puts it belowif-else, so that e.g. a ?? b if c else d is read as (a ?? b) if c else d, which makes perhaps a little more sense than reading it as a ?? (b if c else d).
I don’t object to the PEP’s choices, but placement below not might also make sense. It feels pretty rare to combine ?? with the boolean operators anyway, and I might well parenthesize those cases. At the same time I don’t want to go the JavaScript way, which requires parenthesization in such cases.
There’s another, related issue – associativity, IOW what should a ?? b ?? c do. The PEP doesn’t say. It turns out that regardless of whether you evaluate it as (a ?? b) ?? c or a ?? (b ?? c), you always get the same outcome! (The first non-None value, if any.) But the former, (a ?? b) ?? c evaluates the outcome of a ?? b redundantly even if it is already not None, whereas a ?? (b ?? c) evaluates each at most once, from left to right. The spec implicitly indicates that a ?? b ?? c is a single chain of ?? operators, trying the operands one at a time, stopping at the first not-None value. So we might as well make this more explicit by saying (in the Precedence section) that the operator is considered right-associative. (I also suggest adding an example showing how a ?? b if c else d works.)
To be clear, I don’t think 824 encourages this, I do think 823 does slightly[1]
I’ll put together a realistic example in 823’s thread in response to your question about it there later
I do also agree that there are real patterns this helps with for incoming data, but I also think there are better ways to handle such incoming data once at receiving, and then not being in such a position in the rest of the program if those top level objects are passed around. ↩︎
Piggybacking off of the 6th point, I haven’t seen much support for it so far but I’m personally pretty firmly against a magic method to customize ?? - its usefulness to me lies solely in how strict it is. If a ?? b doesn’t always mean “give me a if it isn’t specifically None, otherwise give me b” and could potentially change depending on the class, we’re back to square one with a or b and this PEP would feel kind of moot.
I really appreciate your post, and I’ve been avoiding saying too much on the PEP 823/824 threads because I really don’t want to even hint at a bias given my (possible) role as one member of an SC that might decide their fates. That said, I’ve put a lot of thought into how I’ll approach deliberation on the PEPs if they are presented to the current SC for decision, which they haven’t yet been. That timeline is totally up to the PEP authors, so maybe I’ll just get to be a spectator when the time comes .
As an SC member, there’s no hiding the fact that I have personal opinions, but whether I lean for or against a PEP, I always try to ask myself “what’s best for Python?” I’m not special among my SC colleagues, all of whom truly do consider every nuance and angle from the same perspective. The wonderful thing about serving with such exceptional people is the way we help each other see past our individual biases, and look at the wider impact, long term view, and overall benefit.
Because the PEPs haven’t been submitted to the SC, we have not yet even discussed them[1]. I have no idea what my fellow SC members think about these PEPs.
So with that preamble, what follows are my personal thoughts.
I think this is because 823 reads more like line noise than 824. One of the things that appealed to me so much about Python, oh 30+ years ago was how cleanly it read, especially as opposed to Perl. Python has been pretty true to that ideal over the years, but to me, 823 feels like a step backward. You can argue that long strings of ?. and ?[] won’t be common, but I’m not so sure. I think we won’t know until the feature is out there, with code bases that can floor on Python 3.16[2].
Especially as my eyes get older, it gets harder to pick out all the little squiggles and smudges which leads to the impression of line noise code. Maybe IDEs with their syntax highlighting and other assistances can help, and maybe in the world of agentic coding it won’t matter anyway, but I still like to read code, and I think 823 will make that harder. I don’t disagree with your blah blah blah observations, but complicated code is complicated. I don’t see how 823 helps readability, and it may make it worse.
It could be, but to me, it’s also the more head-scratch inducing syntax. For a ??= b the PEP defines the semantics as “if a is None, assign b’s value to a”, but it could also incorrectly be interpreted as “assign b to a but only if b is not None”. I don’t think it’s immediately obvious at first glance and I don’t think it’s a slam dunk “learn it once, know it forever”. If the syntax is encountered infrequently, I worry that it’s just cognitively ambiguous enough to slow the reader down, making them have to reason out what it does and why the semantics are as the PEP specifies.
Perhaps, but PEP 308 is nearly 24 years old now, so we have a lot of experience with it. It’s terrible when you have a lot of nested if-else clauses, but that’s often a red flag that the code is trying to be too clever, where spelling it out might be tedious, but improves the readability. And I recall something that you said @guido when you were choosing this exact syntax over all the other alternatives. Paraphrasing[3] it was that you wanted a syntax that was just awkward enough to discourage its common use over plain 'ol if statements. If that’s true, you might not have succeeded .
I actually find this compelling! Try as I might, I’ve never found a good enough synonym for otherwise and as much as I’d prefer the readability of an English word here over an operator, there’s no obviously better choice. In this case, following the herd makes sense.
It definitely gets awkward when trying to clean up data, or traverse JSON fed dictionaries, or deeply nested data structures. I’m just not sure whether the language can help here, or whether a library or functional interface is better. If by “efficient” you mean the operators will allow Python to be faster when traversing complicated data structures, I’m skeptical whether it make any measurable difference in real-world code.
I do think these PEPs elevate None to something even more special than it already is. With sentinel values being introduced in Python 3.15, I wonder if that’s the path we really want to go down. I could imagine for example, some JSON parsing library choosing to define a sentinel that represents “no value was given”, in which case these PEPs don’t help. Are we subtly nudging the choice over to the “just use None” side because it’ll work better with these operators, even if there may be some semantic ambiguity, for example, where None could also represent a present-but-null value? That said, I completely agree with your thoughts against customization.
It’s hard for me to say what’s best, and I’d be pretty inclined to use parentheses anyway to make my intent clear, even if the default semantics would be identical, your subtle evaluation different not withstanding. I do think PEP 824 needs more detail in its section on precedence. I have no doubt you and Marc have deeply thought about what precedence makes the most sense, so some additional clarity and insight into your thinking there would help the PEP.
Anyway, those are some of my dozen few notes as I read the PEPs, mostly in response to your points.
I’ll just close this way too long post by saying that I appreciate how well written the PEPs are and how clearly they make their arguments. I think people are largely in the pro or con camp with these PEPs, and I’m not sure whether anything will make the consensus on them any clearer. That’s just how it is.
trust me, there’s enough to talk about that we routinely bust through our scheduled 90 minute weekly time slot anyway ↩︎
since, assuming this makes it into Python 3.16, if you have to support older Python’s you can’t use this, which is true of all syntax changes ↩︎
None is special. In the past, None has been special enough to deserve its own ??/??= operators. But the new sentinels in 3.15 might make None less special, depending on how they are used. Since the full release is still coming, it’s unclear to me how exactly this will play out.
So, I wonder if it’s best to pause implementing the operators for a little longer (it’s already been 11 years anyway) to see how sentinels change the landscape?
I don’t think there’s any obvious alternative Python could choose, but I also don’t think we box us in too bad with ??. The if x is None pattern is extremely common, ?? will be a great help, and this operator has proven itself in many other languages. Python differs from many other languages in that None appears as an ordinary object, but in all practical respects it’s equivalent to C# null, Perl undef, JS/TS null/undefined.
Personally, I’m very fond of custom sentinels and avoid using None in new APIs, but there’s no good way to extend a ?? operator to arbitrary sentinels.
Semantics like if isinstance(x, ANY_SENTINEL) would be wrong – when using sentinels we want to special-case only exactly that one sentinel by identity. In particular, custom sentinels are useful for being able to treat None as a normal value.
So a more general ?? version would require a way to specify the sentinel, which turns this into a ternary operator (value, sentinel, fallback). Such syntax may be possible (e.g. value ?? fallback could be a shorthand for something atrocious like value ??[None] fallback or value otherwise(None) fallback), but I don’t believe there can ever be agreement on such a syntax.
In practice, what I use instead is a sentinel-aware coalesce() function, something similar to this:
def coalesce[T, D, S = None](value: T|S, default: D, *, sentinel: S = None) -> T|D:
if value is sentinel:
return default
return value
coalesce(value, default) # value ?? default
coalesce(value, default, sentinel=MISSING) # hypothetical ternary
If a None-only ?? operator proves popular and demand exists for custom sentinels, then it could perhaps make sense to add a dedicated method on the standard library sentinel type. E.g.:
MISSING = sentinel("MISSING")
MISSING.coalesce(value, default) # default if value is MISSING else value
Not as convenient as a dedicated operator, but more convenient than an external function or than spelling out the conditional.