[Python-ideas] Null coalescing operators
Steven D'Aprano
steve at pearwood.info
Sat Sep 19 14:06:24 CEST 2015
On Sat, Sep 19, 2015 at 03:17:07AM -0500, C Anthony Risinger wrote:
> I really liked this whole thread, and I largely still do -- I?think -- but
> I'm not sure I like how `?` suddenly prevents whole blocks of code from
> being evaluated. Anything within the (...) or [...] is now skipped (IIUC)
> just because a `?` was added, which seems like it could have side effects
> on the surrounding state, especially since I expect people will use it for
> squashing/silencing or as a convenient trick after the fact, possibly in
> code they did not originally write.
I don't think this is any different from other short-circuiting
operators, particularly `and` and the ternary `if` operator:
result = obj and obj.method(expression)
result = obj.method(expression) if obj else default
In both cases, `expression` is not evaluated if obj is falsey. That's
the whole point.
> If the original example included a `?` like so:
>
> response = json.dumps?({
> 'created': created?.isoformat(),
> 'updated': updated?.isoformat(),
> ...
> })
>
> should "dumps" be None, the additional `?` (although though you can barely
> see it) prevents *everything else* from executing.
We're still discussing the syntax and semantics of this, so I could be
wrong, but my understanding of this is that the *first* question mark
prevents the expressions in the parens from being executed:
json.dumps?( ... )
evaluates as None if json.dumps is None, otherwise it evaluates the
arguments and calls the dumps object. In other words, rather like this:
_temp = json.dumps # temporary value
if _temp is None:
response = None
else:
response = _temp({
'created': None if created is None else created.isoformat(),
'updated': None if updated is None else updated.isoformat(),
...
})
del _temp
except the _temp name isn't actually used. The whole point is to avoid
evaluating an expression (attribute looking, index/key lookup, function
call) which will fail if the object is None, and if you're not going to
call the function, why evaluate the arguments to the function?
> This may cause confusion
> about what is being executed, and when, especially once nesting (to any
> degree really) and/or chaining comes into play!
Well, yes, people can abuse most any syntax.
> Usually when I want to use this pattern, I find I just need to write things
> out more. The concept itself vaguely reminds me of PHP's use of `@` for
> squashing errors.
I had to look up PHP's @ and I must say I'm rather horrified. According
to the docs, all it does is suppress the error reporting, it does
nothing to prevent or recover from errors. There's not really an
equivalent in Python, but I suppose this is the closest:
# similar to PHP's $result = @(expression);
try:
result = expression
except:
result = None
This is nothing like this proposal. It doesn't suppress arbitrary
errors. It's more like a conditional:
# result = obj?(expression)
if obj is None:
result = None
else:
result = obj(expression)
If `expression` raises an exception, it will still be raised, but only
if it is actually evaluated, just like anything else protected by an
if...else or short-circuit operator.
--
Steve
More information about the Python-ideas
mailing list