@@ -19,7 +19,10 @@ This PEP proposes adding two new operators.
1919* The "``None ``-aware indexing" operator ``?[ ] `` ("maybe subscript")
2020
2121Both operators evaluate the left hand side, check if it is not ``None ``
22- and only then evaluate the full expression. They are roughly equal to::
22+ and only then evaluate the full expression. They are roughly equivalent
23+ to:
24+
25+ .. code-block :: python
2326
2427 # a.b?.c
2528 _t1.c if ((_t1 := a.b) is not None ) else None
@@ -80,7 +83,7 @@ modern programming languages have so called "``null``-aware" or
8083ECMAScript (a.k.a. JavaScript) [#js ]_, C# [#csharp ]_, Dart [#dart ]_,
8184Swift [#swift ]_, Kotlin [#kotlin ]_, Ruby [#ruby ]_, PHP [#php ]_ and more.
8285
83- The general idea is to provide a access operators which can traverse
86+ The general idea is to provide access operators which can traverse
8487``null `` or ``None `` values without raising exceptions.
8588
8689Nested objects with ``optional `` attributes
@@ -89,7 +92,7 @@ Nested objects with ``optional`` attributes
8992When writing Python code, it is common to encounter objects with ``optional ``
9093attributes. Accessing attributes, subscript or function calls can raise
9194``AttributeError `` or ``IndexError `` at runtime if the value is ``None ``.
92- Several common patterns have developed to ensure those operators are will
95+ Several common patterns have developed to ensure these operations will
9396not raise. The goal for ``?. `` and ``?[ ] `` is to make reading and writing
9497these expressions much simpler while being predictable and doing the
9598correct things intuitively.
@@ -171,11 +174,11 @@ Writing it like this is correct but, especially for deeply nested
171174object hierarchies, difficult to read and easy to get wrong.
172175
173176Alternative approaches include wrapping the whole expression with
174- a try-except block. While this would also archive the desired
177+ a try-except block. While this would also achieve the desired
175178output, it as well has the potential to introduce errors which
176- might get unnoticed. E.g. if the ``Line.department `` gets deprecated,
177- in the process making it `` optional `` and always return `` None ``, the
178- function would still succeed, even though the input changed significantly.
179+ might get unnoticed. E.g. if the ``Line.department `` attribute gets deprecated, in the process making it `` optional `` and always return
180+ `` None ``, the function would still succeed, even though the input changed
181+ significantly.
179182
180183.. code-block :: python
181184
@@ -186,7 +189,7 @@ function would still succeed, even though the input changed significantly.
186189 return None
187190
188191 Another approach would be to use a ``match `` statement instead. This
189- will work fine but is easy to get wrong as well. It's strongly
192+ will work fine but is easy to get wrong as well. It is strongly
190193recommended to use keyword attributes as otherwise any change in
191194``__match_args__ `` would cause the pattern match to fail.
192195If any attribute names change, the match statement needs to be
@@ -225,8 +228,9 @@ To start, assume each attribute, subscript and function call is
225228 return sensor.machine.line.department.engineer.email[0 ]
226229
227230 Now insert ``? `` after each ``optional `` subexpression. IDEs and most
228- type checkers would be able to help with that since the data structure
229- is strictly typed. *Spaces added for clarity only, though still valid *::
231+ type checkers would often be able to help with that especially if the
232+ data structure is strictly typed. *Spaces added for clarity only,
233+ though still valid *::
230234
231235 def get_person_email(sensor: Sensor) -> str | None:
232236 return sensor.machine? .line? .department.engineer? .email? [0]
@@ -259,8 +263,8 @@ Parsing structured data
259263
260264The ``?. `` and ``?[ ] `` operators can also aid in the traversal of
261265structured data, oftentimes coming from JSON and parsed as nested
262- dicts and lists. It is worth noting though that they do not handle
263- ``missing `` attributes / data. For dictionaries a useful helper is
266+ dicts and lists. It is worth noting though that the operators do not
267+ handle ``missing `` attributes / data. For dictionaries a useful helper is
264268the ``.get(key, default=None) `` method with a default. Depending on
265269the specific use case, pattern matching might also be a viable
266270alternative here.
@@ -408,7 +412,9 @@ access were used.
408412 _t.c() if ((_t := a.b) is not None) else None
409413
410414 # a?.b?.c.d
411- _t2.c.d if ((_t1 := a) is not None) and ((_t2 := t1.b) is not None) else None
415+ _t2.c.d if (
416+ (_t2 := _t1.b if ((_t1 := a) is not None) else None) is not None
417+ ) else None
412418
413419Short-circuiting
414420****************
@@ -501,7 +507,7 @@ AST changes
501507Two new AST nodes are added ``NoneAwareAttribute `` and ``NoneAwareSubscript ``.
502508They are the counterparts to the existing ``Attribute `` and ``Subscript ``
503509nodes. Notably there is no ``expr_context `` attribute because the new nodes
504- do not support assignments itself and thus the context will always be
510+ do not support assignments themselves and thus the context will always be
505511``Load ``.
506512
507513::
@@ -539,7 +545,7 @@ Multiline formatting
539545
540546Using two separate tokens to express ``?. `` and ``?[ `` allows developers
541547to insert a space or line break as needed. For multiline expressions it
542- allows that ``? `` is appended to the ``optional `` subexpression whereas
548+ enables that ``? `` is appended to the ``optional `` subexpression whereas
543549``. `` or ``[ `` could be moved to the next line. This is indented merely
544550as an option for developers. Everyone is free to choose a style that fits
545551their needs, especially code formatters might prefer a style which
@@ -674,7 +680,7 @@ added for attribute and item access automatically.
674680
675681While this might be easier to write at first, it introduces new issues.
676682When using explicit ``?. `` and ``?[ ] `` operators, the input space is well
677- defined. Only ``a ``, ``.c `` and ``d `` are expected to possibly be ``None ``.
683+ defined. Only ``a ``, ``.c `` and ``. d `` are expected to possibly be ``None ``.
678684If ``.b `` all of the sudden is also ``None ``, it would still raise an
679685``AttributeError `` since it was unexpected. That would not happen for
680686``maybe ``. This behavior is problematic since it can subtly hide real
@@ -696,8 +702,8 @@ operators introduced, a unary, postfix operator ``?`` was considered.
696702
697703While this might have made teaching the operators a bit easier, just one
698704instead of two new operators, it may also be **too general **, in a sense
699- that it can be combine with any other operator. It is not clear what the
700- following expressions would mean::
705+ that it can be combine with any other operator. For example it is not
706+ clear what the following expressions would mean::
701707
702708 >>> x? + 1
703709 >>> x? -= 1
@@ -728,7 +734,7 @@ There are a number of libraries which provide some kind of object
728734traversal functions. The most popular likely being ``glom `` [#glom ]_.
729735Others include ``jmespath `` [#jmespath ]_ and ``nonesafe `` [#nonesafe ]_.
730736The idea is usually to pass an object and the lookup attributes as
731- string to a function which handles the rest .
737+ string to a function which handles the evaluation .
732738
733739.. code-block :: python
734740
@@ -744,7 +750,7 @@ It was suggested to add a ``traverse`` or ``deepget`` function to the
744750stdlib. While these libraries do work and have its use cases, especially
745751``glom `` provides an excellent interface to extract and combine multiple
746752data points from deeply nested objects, they do also have some
747- disadvantages. Passing the lookup attributes as string means that often
753+ disadvantages. Passing the lookup attributes as a string means that often
748754times there are no more IDE suggestions. Type checking these expressions
749755is also limited. Furthermore, normal function calls can not provide
750756short-circuiting, so they would still need to be combined with assignment
@@ -839,11 +845,11 @@ either ``?.`` (or ``?:``). Using anything else would be unexpected.
839845Defer ``None ``-aware indexing operator
840846--------------------------------------
841847
842- A point of discussion was the ``?[ ] `` operator. Some though it might be
848+ A point of discussion was the ``?[ ] `` operator. Some thought it might be
843849missed to easily in an expression ``a.b?[c] ``. To move the discussion
844- forward, some suggested to defer the operator for later.
850+ forward, it was suggested to defer the operator for later.
845851
846- While it is often helpful to reduce the scope to move forward at all,
852+ Though it is often helpful to reduce the scope to move forward at all,
847853the ``?[ ] `` operator is necessary to efficiently get items from
848854``optional `` objects. While for dictionary a suitable alternative is to
849855use ``d?.get(key) ``, for general objects developers would have needed
@@ -939,15 +945,15 @@ As shown in the `Motivation`_ section, the operators are not designed
939945to handle arbitrary data, rather to make it easier to work with nested
940946objects with ``optional `` attributes. If arbitrary data handling is the
941947goal, other language concepts are likely better suited, like try-except,
942- a match statement or different data traversal libraries from PyPI.
948+ a match statement or data traversal libraries from PyPI.
943949
944950See the `Exception-aware operators `_ section for more details why
945951this was rejected.
946952
947953Just use ...
948954------------
949955
950- A comment reply towards the proposal was to use an existing language
956+ A common reply towards the proposal was to use an existing language
951957concept instead of introducing a syntax. A few alternatives have been
952958proposed.
953959
0 commit comments