[Python-ideas] PEP 505 (None coalescing operators) thoughts
Nick Coghlan
ncoghlan at gmail.com
Thu Oct 1 09:59:11 CEST 2015
On 1 October 2015 at 12:39, Andrew Barnert via Python-ideas
<python-ideas at python.org> wrote:
> On Sep 30, 2015, at 18:08, MRAB <python at mrabarnett.plus.com> wrote:
>>
>> It's only just occurred to me that there's a small inconsistency here.
>> The "?.", "?[" and "?(" will short-circuit on None, whereas the "??"
>> will short-circuit on non-None.
>>
>> Would that cause any confusion in practice?
>
> I noticed this when I was trying to write out grammar, sample ASTs, and sample bytecode for these things. I went searching the thread and saw no one had pointed it out. I went through docs and blogs for other languages, and didn't see anyone pointing out, complaining about, or offering to clear up any confusion. So I figured I wouldn't mention it, and see if anyone else even noticed.
>
> The fact that an experienced programmer like you didn't even notice it until after a zillion messages over a span of weeks, and apparently nobody else did at all, seems to imply that there's no dangerously misleading intuitive parallel here.
>
> (By the way, I have a feeling that including ?= will increase the risk of confusion, but I have no idea where that feeling comes from, so it may just be random noise in my head, maybe because I like ?. but don't particularly like ?= or something...)
Because unlike "?.", "?[" and "?(", "?=" would also shortcircuit on
"if not None" if expanded as a ternary expression:
target = target if target is not None else default
However, it's possible to make it consistent by instead expanding it as:
if target is None: target = default
As a result, one interesting way of looking at this problem is to ask:
what if we *only* offered the conditional operations, *without*
offering a binary null-coalescing operator?
That is, we could allow conditional assignment:
data ?= []
headers ?= {}
arg ?= make_default()
With the meaning:
if data is None: data = []
if headers is None: headers = {}
if arg is None: arg = make_default()
That would deal directly with the idiomatic case of handling a default
argument of "None" and replacing it with a mutable container or
expensive to calculate default value.
Multiple levels of coalescence would need to be spelled out as
multiple statements rather than as chained binary operations (in this
case, I think putting it all on one line helps make the "first
non-None value wins" semantics clearer):
title ?= user_title; title ?= local_default_title; title ?=
global_default_title
The semantics of dict.setdefault() could also potentially be made
clearer, as "d[key] ?= make_default()" could become a short circuiting
equivalent of "dict.setdefault(k, make_default())":
try:
d[key]
except KeyError:
d[key] = make_default()
Custom sentinels would still need to be spelled out with an if statement:
if arg is _sentinel:
arg = make_default()
If they were offered, conditional attribute access, conditional item
lookup and conditional calls would use the same "LHS is None" check,
but in a ternary expression rather than an if statement, with the
following:
title?.upper()
person?['name']
func?(arg)
being equivalent to:
None if title is None else title.upper()
None if person is None else person['name']
None if func is None else func(arg)
Regards,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
More information about the Python-ideas
mailing list