freenode
AnalysisLanguages & Toolchains

None-aware operators reopen Python’s syntax and teachability fights

Companion PEPs 823 and 824 force the community to weigh everyday ergonomics against teaching cost, precedence surprises, and sentinel interactions under Steering Council scrutiny.

Python’s long resistance to new syntax is under fresh pressure. Two companion PEPs revive a debate that began with PEP 505, arriving just as the Steering Council process itself draws sharper community attention to every permanent change in the language surface. The question is no longer only whether None-aware operators are convenient; it is whether the cost of teaching, parsing, and reasoning about them is justified in a language that still prizes explicitness.

PEP 823 proposes the None-aware attribute and indexing operators ?. and ?[]. PEP 824 proposes the None-coalescing operators ?? and ??= . Both were advanced together by cdce8p, who framed them as the practical successors to years of discussion. The operators short-circuit on None rather than on truthiness. A chain such as a.b?.c.d expands roughly to a temporary that is tested with “is not None” before the remainder is evaluated; otherwise the whole expression yields None. The coalescing form a ?? b returns a when it is not None and otherwise evaluates b. The assignment form ??= writes only when the target is None. An interactive demo accompanied the launch, and both PEPs have been submitted to the Steering Council.

Proponents treat the operators as relief from boilerplate that real codebases already accumulate. Nested attribute or index chains that may contain None force either ladders of if statements or walrus-assisted ternaries. Traversal helper functions were examined and found wanting for anything beyond simple dotted paths: they cannot short-circuit the tail of a complex expression that itself contains calls, comprehensions, or further operators, and they forfeit IDE completion and static typing. The PEPs therefore position the new syntax as the only mechanism that preserves both short-circuiting and ordinary Python expression structure.

Skeptics answer that the sugar is not free. Guido van Rossum has repeatedly weighed the teaching burden, the interaction with custom sentinels, and the need for clear precedence rules. After the PEPs reached the Council, Barry Warsaw asked the authors to record an explicit rationale: he wanted to be able to say “the authors thought about the different options, settled on option X for the reasons given.” The updated PEP 824 now carries that extended discussion in its Rejected Ideas section. ThomasP still found the chosen order surprising: “the precedence of or being greater than ?? is confusing for me.” In an expression such as x ?? y or z the evaluation order does not match the intuition that or should apply to the whole coalesced result. Guido floated a procedural alternative: “ask the SC to pick between two proposals (e.g. current PEP and JS/TS).”

Concrete edge cases keep the argument technical rather than purely aesthetic. Parentheses deliberately stop propagation, so that (a?.b).c behaves exactly like an intermediate assignment followed by ordinary attribute access. Pyprohly stated the rule cleanly: “The reason the parentheses interrupt propagation is so that (a?.b).c behaves the same as _v = a?.b; _v.c .” Brénainn Woodsend observed that the parenthesized form is “effectively nonsense code” in isolation, yet the interruption is required so that a call such as somefun(a?.b).c does not unexpectedly swallow later attributes. Outside the access chain the operators do not suppress errors: if a is None then a?.b + 1 still raises TypeError, a point Lucas Malor and others confirmed is intentional and outside the PEPs’ scope.

Further objections have already shaped the documents. Petr Viktorin noted that IPython uses ? and ?? for help display; testing showed the magic commands remain distinct from the new operators and continue to function. Custom sentinels (PEP 661) and a possible No-Value protocol appear among the common objections; the authors rejected both broader None-aware binary operators and a lone ? meaning “is not None.” On the traversal-library alternative, Woodsend remained unconvinced that a nearly invisible ? inside a dense line is clearer than an ordinary if: “I’d struggle to see the one tiny ? character that no-ops significant chunks of what must already be a pretty dense line of code.”

The two threads therefore form a single story. Ergonomic pressure from languages that already ship null-aware operators collides with Python’s historical preference for constructs whose meaning can be explained without new precedence tables or special cases for sentinels. Earlier syntax additions (assignment expressions, match statements) raised the same trade-off; each succeeded only after the teaching and tooling costs were judged acceptable. The present PEPs sit at the same threshold, now under formal Steering Council review.

What remains unresolved is not merely acceptance or rejection. Precedence may still shift, a JS-like alternative may be requested, or the Council may decide that the reduction in None-related exceptions does not outweigh the permanent increase in language surface. The debate is less about any single operator than about the criteria Python will use, under its current governance, when everyday convenience asks for new syntax.