[Python-checkins] peps: Create PEP 505 for None-aware operators

chris.angelico python-checkins at python.org
Sat Sep 19 04:11:08 CEST 2015


https://hg.python.org/peps/rev/bdd5fc534c29
changeset:   6068:bdd5fc534c29
user:        Chris Angelico <rosuav at gmail.com>
date:        Sat Sep 19 12:10:53 2015 +1000
summary:
  Create PEP 505 for None-aware operators

files:
  pep-0505.txt |  199 +++++++++++++++++++++++++++++++++++++++
  1 files changed, 199 insertions(+), 0 deletions(-)


diff --git a/pep-0505.txt b/pep-0505.txt
new file mode 100644
--- /dev/null
+++ b/pep-0505.txt
@@ -0,0 +1,199 @@
+PEP: 505
+Title: None coalescing operators
+Version: $Revision$
+Last-Modified: $Date$
+Author: Mark E. Haase <mehaase at gmail.com>
+Status: Draft
+Type: Standards Track
+Content-Type: text/x-rst
+Created: 18-Sep-2015
+Python-Version: 3.6
+
+Abstract
+========
+
+Several modern programming languages have so-called "null coalescing" or
+"null aware" operators, including C#, Dart, Perl, Swift, and PHP (starting in
+version 7). These operators provide syntactic sugar for common patterns
+involving null references. [1]_ [2]_
+
+* The "null coalescing" operator is a binary operator that returns its first
+  first non-null operand.
+* The "null aware member access" operator is a binary operator that accesses
+  an instance member only if that instance is non-null. It returns null
+  otherwise.
+* The "null aware index access" operator is a binary operator that accesses a
+  member of a collection only if that collection is non-null. It returns null
+  otherwise.
+
+Python does not have any directly equivalent syntax. The ``or`` operator can
+be used to similar effect but checks for a truthy value, not ``None``
+specifically. The ternary operator ``... if ... else ...`` can be used for
+explicit null checks but is more verbose and typically duplicates part of the
+expression in between ``if`` and ``else``. The proposed ``None`` coalescing
+and ``None`` aware operators ofter an alternative syntax that is more
+intuitive and concise.
+
+
+Rationale
+=========
+
+Null Coalescing Operator
+------------------------
+
+The following code illustrates how the ``None`` coalescing operators would
+work in Python::
+
+    >>> title = 'My Title'
+    >>> title ?? 'Default Title'
+    'My Title'
+    >>> title = None
+    >>> title ?? 'Bar'
+    'Default Title'
+
+Similar behavior can be achieved with the ``or`` operator, but ``or`` checks
+whether its left operand is false-y, not specifically ``None``. This can lead
+to surprising behavior. Consider the scenario of computing the price of some
+products a customer has in his/her shopping cart::
+
+    >>> price = 100
+    >>> requested_quantity = 5
+    >>> default_quantity = 1
+    >>> (requested_quantity or default_quantity) * price
+    500
+    >>> requested_quantity = None
+    >>> (requested_quantity or default_quantity) * price
+    100
+    >>> requested_quantity = 0
+    >>> (requested_quantity or default_quantity) * price # oops!
+    100
+
+This type of bug is not possible with the ``None`` coalescing operator, 
+because there is no implicit type coersion to ``bool``::
+
+    >>> price = 100
+    >>> requested_quantity = 0
+    >>> default_quantity = 1
+    >>> (requested_quantity ?? default_quantity) * price
+    0
+
+The same correct behavior can be achieved with the ternary operator. Here is
+an excerpt from the popular Requests package::
+
+    data = [] if data is None else data
+    files = [] if files is None else files
+    headers = {} if headers is None else headers
+    params = {} if params is None else params
+    hooks = {} if hooks is None else hooks
+
+This particular formulation has the undesirable effect of putting the operands
+in an unintuitive order: the brain thinks, "use ``data`` if possible and use
+``[]`` as a fallback," but the code puts the fallback _before_ the preferred
+value.
+
+The author of this package could have written it like this instead::
+
+    data = data if data is not None else []
+    files = files if files is not None else []
+    headers = headers if headers is not None else {}
+    params = params if params is not None else {}
+    hooks = hooks if hooks is not None else {}
+
+This ordering of the operands is more intuitive, but it requires 4 extra
+characters (for "not "). It also highlights the repetition of identifiers:
+``data if data``, ``files if files``, etc. The ``None`` coalescing operator
+improves readability::
+
+    data = data ?? []
+    files = files ?? []
+    headers = headers ?? {}
+    params = params ?? {}
+    hooks = hooks ?? {}
+
+The ``None`` coalescing operator also has a corresponding assignment shortcut.
+
+::
+
+    data ??= []
+    files ??= []
+    headers ??= {}
+    params ??= {}
+    hooks ??= {}
+
+The ``None`` coalescing operator is left-associative, which allows for easy
+chaining::
+
+    >>> user_title = None
+    >>> local_default_title = None
+    >>> global_default_title = 'Global Default Title'
+    >>> title = user_title ?? local_default_title ?? global_default_title
+    'My Title'
+
+The direction of associativity is important because the ``None`` coalesing
+operator short circuits: if it's left operand is non-null, then the right
+operand is not evaluated.
+
+    >>> def get_default(): raise Exception()
+    >>> 'My Title' ?? get_default()
+    'My Title'
+
+
+Null-Aware Member Access Operator
+---------------------------------
+
+    >>> title = 'My Title'
+    >>> title.upper()
+    'MY TITLE'
+    >>> title = None
+    >>> title.upper()
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+    AttributeError: 'NoneType' object has no attribute 'upper'
+    >>> title?.upper()
+    None
+
+
+Null-Aware Index Access Operator
+---------------------------------
+
+    >>> person = {'name': 'Mark', 'age': 32}
+    >>> person['name']
+    'Mark'
+    >>> person = None
+    >>> person['name']
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+    TypeError: 'NoneType' object is not subscriptable
+    >>> person?['name']
+    None
+
+
+Specification
+=============
+
+
+References
+==========
+
+.. [1] Wikipedia: Null coalescing operator
+   (https://en.wikipedia.org/wiki/Null_coalescing_operator)
+
+.. [2] Seth Ladd's Blog: Null-aware operators in Dart
+   (http://blog.sethladd.com/2015/07/null-aware-operators-in-dart.html)
+
+
+Copyright
+=========
+
+This document has been placed in the public domain.
+
+
+

+..
+   Local Variables:
+   mode: indented-text
+   indent-tabs-mode: nil
+   sentence-end-double-space: t
+   fill-column: 70
+   coding: utf-8
+   End:

-- 
Repository URL: https://hg.python.org/peps


More information about the Python-checkins mailing list