[Rhodri James]
We could argue about how intuitive or not these operators are, but they
are used in other languages, so they clearly aren't completely unintuitive.
Other languages are other languages. Other languages use the "<condition> ?
<expr_a> : <expr_b>" form of the the ternary operator. That doesn't mean we
should adopt that into Python.
But it does and did mean that we should consider it when coming up with Python's version of the ternary operator. *It isn't unintuitive* and neither is ?? None-aware operators may be bad for other reasons, but that argument doesn't wash.
No. The fact that other languages use it doesn't mean that "it isn't unintuitive". The fact that other languages use it doesn't mean that it doesn't harm readability. The fact that other languages use it has pretty much no bearing on any of that. What it actually means is: other languages are trying it out so we have the luxury of seeing if they regret it in the future or if it becomes a popular and well loved feature before integrating it into Python.
I believe it's counter-intuitive because '?' always comes at the end of an english sentence, so it reads like other clause-ending operators like the ")" on a function call or the "]" at the end of a index operation, then the "." reads like a follow-on.
And no; you can't say the same thing about "." always coming at the end of a sentence because:
a) that's not true (e.g. i.e, e.g., etc.)
b) "." attribute access has many decades of precedence
c) a "." is less visually obtrusive than a "?".
[Rhodri James]
Pardon? It composes exactly like any other in-place operator.
Yes. I'll concede. I was wrong about that.
[Rhodri James]
yes, you do need to explain how merely being high precedence helps here. Let's start small; please explain programmatically what
name?
does as an expression. Once we have that down, we can move on to
person.(name?)
Sure. First, I would suggest we define a new class NoneException(BaseException), and derive new Exception multi-classes like: class NoneAttributeError(AttributeError, NoneException).
Then I would make those the exceptions thrown by attribute errors on the None object. That way you can conceptualize the '?' operator is to the try-except statement as the ternary operator is to if-else statements EXCEPT that the "except" part of the try-except block is implicitly filled in with "except NoneException: return None" so in general:
<expression>?Would parse to:
try:
result = eval(expression)
except NoneException:
result = NoneSimilar to how:
<expression_1> if <condition_expression> else <expression_2>Parses to:
if eval(condition_expression):
result = eval(expression_1)
else:
result = eval(expression_2)More specifically:
name?Wouldn't do much:
try:
result = name
except NoneException:
result = NoneAnd:
person.(name?)
would throw a SyntaxError because
person.( is not valid syntax regardless of my proposal.Perhaps you meant something like:
(person.name).first?In that case;
person.name would evaluate to some result or throw an un-guarded AttributeError, in the former case the <expression> to the left of the '?' is an attribute access on the result of (
person.name). In the latter case; an AttributeError is thrown.
That seems pretty straight forward. No?
On 29/07/18 16:12, Abe Dillon wrote:
> spam?.eggs.cheese.aardvark # why would you ever do this?
If you knew that if you really have something in "spam", your program guarantees it will have an "eggs" attribute with a "cheese" attribute, etc, you just don't know if "spam" is not None. It's the same insider knowledge you would use if you wrote it out as
spam.eggs.cheese.aardvark if spam is not None else None
That's not the equivalent as described in PEP 505. If "spam" is None, "spam?.eggs" evaluates to None without throwing an AttributeError, but the rest of the expression will throw an AttributeError from trying to access "None.cheese".