[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