@@ -134,12 +134,23 @@ correct things intuitively.
134134
135135 A simple function which will most likely work just fine. However, there
136136are a few subtle issues. For one each condition only checks for truthiness.
137- Would for example ``Machine `` overwrite ``__eq__ `` to return ``False `` at
137+ Would for example ``Machine `` overwrite ``__bool__ `` to return ``False `` at
138138some point, the function would just return ``None ``. This is problematic
139139since ``None `` is a valid return value already. Thus this would not raise
140140an exception in the caller and even type checkers would not be able to
141141detect it. The solution here is to compare with ``None `` instead.
142142
143+ .. note ::
144+
145+ It is assumed that if ``Person.emails is not None ``, it will
146+ always contain at least one item. This is done in order to avoid
147+ confusion around potential error cases. The goal for this PEP is to
148+ make the ``[ ] `` operator safe for ``optional `` attributes which
149+ could raise a ``TypeError ``. It is not to simplify accessing
150+ elements in a sequence of unknown length which could raise an
151+ ``IndexError `` instead. See `Add list.get(key, default=None) `_ in
152+ the deferred ideas section for that.
153+
143154.. code-block :: python
144155
145156 def get_person_email (sensor : Sensor) -> str | None :
@@ -176,7 +187,8 @@ object hierarchies, difficult to read and easy to get wrong.
176187Alternative approaches include wrapping the whole expression with
177188a try-except block. While this would also achieve the desired
178189output, it as well has the potential to introduce errors which
179- might get unnoticed. E.g. if the ``Line.department `` attribute gets deprecated, in the process making it ``optional `` and always return
190+ might get unnoticed. E.g. if the ``Line.department `` attribute gets
191+ deprecated, in the process making it ``optional `` and always return
180192``None ``, the function would still succeed, even though the input changed
181193significantly.
182194
@@ -193,7 +205,7 @@ will work fine but is easy to get wrong as well. It is strongly
193205recommended to use keyword attributes as otherwise any change in
194206``__match_args__ `` would cause the pattern match to fail.
195207If any attribute names change, the match statement needs to be
196- updated as well. IDEs can not reliably do that themselves since a
208+ updated as well. Even IDEs can not reliably do that themselves since a
197209class pattern is not restricted to existing attributes and can instead
198210match any possible name. For sequence patterns it is also necessary
199211to remember the wildcard match. Lastly, using ``match `` is significantly
@@ -228,9 +240,8 @@ To start, assume each attribute, subscript and function call is
228240 return sensor.machine.line.department.engineer.email[0 ]
229241
230242 Now insert ``? `` after each ``optional `` subexpression. IDEs and most
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 *::
243+ type checkers will be able to help with identifying these. *Spaces added
244+ for clarity only, though still valid *::
234245
235246 def get_person_email(sensor: Sensor) -> str | None:
236247 return sensor.machine? .line? .department.engineer? .email? [0]
@@ -337,7 +348,17 @@ Other common patterns
337348A collection of additional patterns which could be improved with
338349``?. `` and ``?[ ] ``. It is not the goal to list every foreseeable
339350option but rather to help recognize these patterns which often
340- hide in plain side. Attribute and function names have been shortened:
351+ hide in plain sight. Attribute and function names have been shortened.
352+
353+ .. note ::
354+
355+ Most patterns below are **not ** fully identical. As mentioned
356+ `earlier <Nested objects with optional attributes _>`_, it is common
357+ to use boolean expressions to filter out ``None `` values. Other
358+ falsy values, e.g. ``False ``, ``"" ``, ``0 ``, ``[] ``, ``{} `` or custom
359+ objects which overwrite ``__bool__ ``, are filtered out too though.
360+ If code relied on this property, the expression cannot necessarily
361+ be replaced with ``?. `` or ``.[ ] ``.
341362
342363::
343364
@@ -367,15 +388,16 @@ hide in plain side. Attribute and function names have been shortened:
367388 a.b and a.b[0].c and a.b[0].c.d and a.b[0].c.d[0].e
368389 a.b?[0].c?.d?[0].e
369390
370- d : dict
371- d and key in d and d [key]
372- d ?.get(key)
391+ d1 : dict | None
392+ d1 and key in d1 and d1 [key]
393+ d1 ?.get(key)
373394
374- key in d and d[key][other]
395+ d2: dict
396+ key in d2 and d2[key][other]
375397 d.get(key)?[other]
376398
377- key in d and d [key].do_something()
378- d .get(key)?.do_something()
399+ key in d2 and d2 [key].do_something()
400+ d2 .get(key)?.do_something()
379401
380402 (c := a.b) and c.startswith(key)
381403 a.b?.startswith(key)
@@ -455,13 +477,13 @@ is broken once a (soft-) keyword is reached.
455477Grouping
456478********
457479
458- Using ``?. `` and ``?[ ] `` inside groups is possible. Short-circuiting
459- will be broken either by the rules laid out in the `Short-circuiting `_
460- section or at the end of a group. For example the expression `` (a?.b).c ``
461- will raise an ``AttributeError `` on ``.c `` if `` a = None ``. This is
462- conceptually identical to extracting the group contents and storing the
463- result in a temporary variable before substituting it back into the
464- original expression.
480+ Using ``?. `` and ``?[ ] `` inside groups is possible. In addition to the
481+ rules laid out in the `previous < Short-circuiting _>`_ section,
482+ short-circuiting will also be broken at the end of a group. For example
483+ the expression `` (a?.b).c `` will raise an ``AttributeError `` on ``.c ``
484+ if `` a = None ``. This is conceptually identical to extracting the group
485+ contents and storing the result in a temporary variable before
486+ substituting it back into the original expression.
465487
466488::
467489
@@ -693,7 +715,7 @@ would be raised under normal circumstances. Instead of testing for
693715expression. Similar to nested try-except blocks.
694716
695717While this would technically work, it's not at all clear what the result
696- should be if an error is caught. Furthermore this approach would hide
718+ should be if an error is caught. Furthermore, this approach would hide
697719genuine issues like a misspelled attribute which would have raised an
698720``AttributeError ``. There are also already established patterns to
699721handle these kinds of errors in the form of ``getattr `` and
@@ -899,9 +921,14 @@ Some comments suggested to use existing syntax like ``->`` for the
899921``None ``-aware access operators, e.g. ``a->b.c ``.
900922
901923Though possible, the ``-> `` operator is already used in Python for
902- something completely different. Additionally, "``null ``-aware" or
903- "optional chaining" operators in other languages almost exclusively use
904- either ``?. `` (or ``?: ``). Using anything else would be unexpected.
924+ something completely different. Additionally, a majority of other
925+ languages which support "``null ``-aware" or "optional chaining"
926+ operators use ``?. ``. Some exceptions being Ruby [#ruby ]_ with ``&. ``
927+ or PHP [#php ]_ with ``?-> ``. The ``? `` does not have an assigned
928+ meaning in Python just yet. As such it makes sense to adopt
929+ the most common spelling for the ``None ``-aware access operators.
930+ Especially considering that it also works well with the "normal"
931+ ``. `` and ``[ ] `` operators.
905932
906933Defer ``None ``-aware indexing operator
907934--------------------------------------
@@ -933,7 +960,7 @@ An earlier version of this PEP suggested the short-circuiting
933960behavior should be indifferent towards grouping. It was assumed that
934961short-circuiting would be broken already for more complex group
935962expressions like ``(a?.b or c).d `` by the behavior outline in the
936- `Short-circuiting `_ section. While for simpler ones like ``(a?.b).c ``
963+ `Short-circuiting `_ section, while for simpler ones like ``(a?.b).c ``
937964the grouping was considered trivial and the expression would be equal to
938965``a?.b.c ``. The advantage being that developers would not have to
939966look for groupings when evaluating simpler expressions. As long as
@@ -984,7 +1011,7 @@ reading it again is far from simple.
9841011
9851012In contrast, ``?. `` and ``?[ ] `` leave the core of the expression mostly
9861013untouched. It is thus fairly strait forward to see what is happening.
987- Furthermore it will be easier to write since one can start from the
1014+ Furthermore, it will be easier to write since one can start from the
9881015normal attribute access and subscript operators and just insert ``? ``
9891016as needed. The Python error messages for accessing a member or subscript
9901017of ``None `` can help here, similarly type checkers and IDEs will be
@@ -1140,7 +1167,7 @@ Some mentioned that ``None`` is not special enough to warrant dedicated
11401167operators.
11411168
11421169"``null ``-aware" or "optional chaining" operators have been added to a
1143- number of other modern programming languages. Furthermore adding
1170+ number of other modern programming languages. Furthermore, adding
11441171``None ``-aware access operators is something which was suggested numerous
11451172times since :pep: `505 ` was first proposed ten years ago.
11461173
@@ -1160,7 +1187,7 @@ While this is true, the use of ``?.`` and ``?[ ]`` for "null"- /
11601187``None ``-aware operators in other languages means that it would be
11611188difficult to us ``? `` for anything else.
11621189
1163- Furthermore it is common for developers to use / be fluent in multiple
1190+ Furthermore, it is common for developers to use / be fluent in multiple
11641191programming languages. It is up the Python language specification to
11651192provide a meaning for these operators which roughly matches those in
11661193other languages while still respecting the norms in Python itself.
0 commit comments