From 31e36296932e56431be0ca883b86c017d4b24d53 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 19 Oct 2026 13:37:38 -0700 Subject: [PATCH 1/6] PEP 824: Expand 'How to Teach This' to a mini-tutorial --- peps/pep-0824.rst | 100 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index d0943b0bf61..5d2d0d9c987 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -307,20 +307,92 @@ There are no new security implications from this proposal. How to Teach This ================= -In a practical sense it might be helpful to think of the "``None``-coalescing" -operator ``??`` as a special case for the conditional ``or`` -operator, with the caveat that ``??`` checks for ``is not None`` instead -of truthiness. As such it makes sense to include ``??`` when teaching -the other conditional operators ``and`` and ``or``. - -The "``None``-coalescing assignment" operator ``??=`` can be best -thought of as a conditional assignment operator. As it is closely -related to ``??``, explaining these together would make sense. -Though it looks related to binary assignment operators like ``+=`` -as well, it is worth pointing out the distinction between these. -Since ``??=`` is a **conditional** assignment operator, the right-hand -side will be skipped entirely in some case, while ``+=`` always -evaluates both sides. +Prerequisites +------------- + +Students should already know how the short-circuit operators ``and`` and +``or`` work, and what counts as truthy and falsy values. + +Basics +------ + +The ``??`` operator is a short-circuit operator like ``and`` and ``or`` +that only checks whether its left argument is ``None``. The others check +for truthiness or falsiness, and treat values like ``0``, ``""`` and ``[]`` +as falsy. + +``a ?? b`` is equivalent to ``a if a is not None else b``, except that +``a`` is only evaluated once. Similarly, ``a or b`` is equivalent to +``a if a else b``, with ``a`` evaluated only once. + +For example, ``d.get(key) ?? make_default(key)`` is roughly equivalent to +``d.get(key, make_default(key))``, with two differences: + +* ``??`` only evaluates ``make_default(key)`` when needed, whereas the + function call used as the default argument to ``dict.get`` is always + evaluated. +* ``??`` also returns the default when ``d[key]`` is ``None``, whereas + ``dict.get`` returns ``None`` in that case. + +The ``??=`` operator is the conditional assignment operator derived from +``??``. ``a ??= b`` has the same effect as ``a = a ?? b``, except that +there is no assignment when ``a`` is not ``None``. Like augmented +assignment, it evaluates the target only once. For example, in +``a[index()] ??= b``, ``index()`` is called only once. The right-hand +side is evaluated only when the target's value is ``None``. + +Chaining +-------- + +An expression like ``a ?? b ?? c ?? d`` evaluates its subexpressions +(``a``, ``b``, etc.) from left to right until it finds a value that is +not ``None`` or reaches the end. If all values are ``None``, the result +is ``None``. + +Adding parentheses does not change this: ``(a ?? b) ?? c`` has the same +meaning as ``a ?? (b ?? c)``, since ``??`` is an associative operator. + +Precedence +---------- + +The ``??`` operator has a lower precedence than ``or`` and ``and``. +Thus, ``a or b ?? c`` means ``(a or b) ?? c``, and ``a ?? b and c`` +means ``a ?? (b and c)``. To get the opposite grouping, use +``a or (b ?? c)`` or ``(a ?? b) and c``. + +This precedence is a convention. For clarity, we recommend always adding +parentheses when mixing ``??`` with ``and`` or ``or``. + +When to use +----------- + +This is useful when you find yourself writing expressions like +``a if a is not None else b`` a lot. The ``??=`` operator is handy for +providing defaults for arguments. For example: + +.. code-block:: python + + def add_default_user(users: list[User] | None = None) -> list[User]: + if users is None: + users = [] + users.append(create_default_user()) + return users + +This can be compacted to: + +.. code-block:: py824 + + def add_default_user(users: list[User] | None = None) -> list[User]: + users ??= [] + users.append(create_default_user()) + return users + +When not to use +--------------- + +Do not use these operators when you also want to check for other falsy +values. For example, ``name ?? "unknown"`` preserves an empty string, +whereas ``name or "unknown"`` replaces ``""`` with ``"unknown"``. Reading expressions out loud ---------------------------- From 47ac704ac34c1c72986f5a8795dcd3c90273698f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 19 Oct 2026 13:46:46 -0700 Subject: [PATCH 2/6] Rename Precedence subsection to Operator precedence, to avoid implicit link target confusion --- peps/pep-0824.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index 5d2d0d9c987..13f8b690525 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -352,8 +352,8 @@ is ``None``. Adding parentheses does not change this: ``(a ?? b) ?? c`` has the same meaning as ``a ?? (b ?? c)``, since ``??`` is an associative operator. -Precedence ----------- +Operator precedence +------------------- The ``??`` operator has a lower precedence than ``or`` and ``and``. Thus, ``a or b ?? c`` means ``(a or b) ?? c``, and ``a ?? b and c`` From 5d7d5d625c15e60812e75d99ef12e9a6d1af102d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 20 Oct 2026 10:25:27 -0700 Subject: [PATCH 3/6] Add Change History entry --- peps/pep-0824.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index 13f8b690525..479b4042879 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -684,6 +684,12 @@ Footnotes (https://perldoc.perl.org/perlop#Assignment-Operators) +Change History +============== + +* 20-Oct-2026: Turned `How to Teach This`_ into a mini-tutorial. + + Copyright ========= From 554170e8906bb711ad42c33fc9f97d152cc7f641 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 20 Oct 2026 10:41:35 -0700 Subject: [PATCH 4/6] Clarify para about d.get(key) ?? make_default() --- peps/pep-0824.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index 479b4042879..aa0bb459623 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -325,14 +325,10 @@ as falsy. ``a`` is only evaluated once. Similarly, ``a or b`` is equivalent to ``a if a else b``, with ``a`` evaluated only once. -For example, ``d.get(key) ?? make_default(key)`` is roughly equivalent to -``d.get(key, make_default(key))``, with two differences: - -* ``??`` only evaluates ``make_default(key)`` when needed, whereas the - function call used as the default argument to ``dict.get`` is always - evaluated. -* ``??`` also returns the default when ``d[key]`` is ``None``, whereas - ``dict.get`` returns ``None`` in that case. +For example, ``d.get(key) ?? make_default()`` calls ``make_default()`` +only when the key is missing or its value is ``None``. By contrast, +``d.get(key, make_default())`` always evaluates ``make_default()``, and +still returns ``None`` when the key is present with a ``None`` value. The ``??=`` operator is the conditional assignment operator derived from ``??``. ``a ??= b`` has the same effect as ``a = a ?? b``, except that From ee13ff0b4b9fdac1c3186b16cdfb90a37b38d989 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 20 Oct 2026 10:45:52 -0700 Subject: [PATCH 5/6] Clarify associativity para --- peps/pep-0824.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index aa0bb459623..b221d0954b2 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -345,8 +345,8 @@ An expression like ``a ?? b ?? c ?? d`` evaluates its subexpressions not ``None`` or reaches the end. If all values are ``None``, the result is ``None``. -Adding parentheses does not change this: ``(a ?? b) ?? c`` has the same -meaning as ``a ?? (b ?? c)``, since ``??`` is an associative operator. +Like ``or``, ``??`` is associative: ``(a ?? b) ?? c`` has the same +meaning as ``a ?? (b ?? c)``. Operator precedence ------------------- From 9926a72d3e2b4b2129847f797e801d89bf12d133 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 20 Oct 2026 10:56:38 -0700 Subject: [PATCH 6/6] Mention that ?? can also replace incorrect truth/falsy checks --- peps/pep-0824.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index b221d0954b2..00476d24033 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -363,8 +363,12 @@ When to use ----------- This is useful when you find yourself writing expressions like -``a if a is not None else b`` a lot. The ``??=`` operator is handy for -providing defaults for arguments. For example: +``a if a is not None else b`` a lot. It can also replace +``a if a else b`` and ``a or b`` when a ``None`` check is intended: +use ``a ?? b`` for either. + +The ``??=`` operator is handy for providing defaults for arguments. +For example: .. code-block:: python