(Thu Mar 6 23:26:47 CET 2014) Chris Angelico responded:
On Fri, Mar 7, 2014 at 7:29 AM, Jim J. Jewett <jimjjewett at gmail.com> wrote:
[ note that "x if y" already occurs in multiple contexts, and always evaluates y before x. ]
Yes, but that's still out of order.
Yeah, but local consistency is more important than global guidelines. :D
... *re*-using out-of-order-"if" shouldn't add any additional costs.
The other thing to note is that it's somewhat ambiguous. Until you find that there isn't an else clause, it could be the equally valid "expr except (default if cond else other_default)", with the actual "if Exception" part still to come.
True -- and I'm not a parser expert. But my belief is that the current parser allows lookahead for exactly one token, and that the "else" would fit within that limit.
... humans reading the code have to assume style guides mightn't be followed.
True ... but I hope any non-trivial use of this (including use with a non-trivial ternary if) will look bad enough to serve as its own warning.
The advantages of this form get much stronger with [as e] or multiple different except clauses, but some of them do apply to even the simplest form.
Multiple different except clauses would make for an even messier evaluation order:
expr1 except expr3 if expr2 except expr5 if expr4
If you consider the exception type to be the condition, then this makes sense (that is, if you read it as "if isinstance(thrown_exception, Exception)"); [but the most obvious reading is boolean; as always True]
I phrased that badly. I agree that without parentheses for good spacing, the above is at least ambiguous -- that is what you get for stringing multiple clauses together without internal grouping. I do think parentheses help, (but are less important when there is only a single "if") and I strongly prefer that they be internal (which you fear looks too much like calling a function named except). In that case, it is: expr1 except (expr3 if expr2) and the extension to multiple except clauses would be: expr1 except (expr3 if expr2, expr5 if expr4) though as I discuss later, placing parentheses there also makes a colon or arrow more tolerable. It does this because the nearby parens make it look more like the existing (non-lambda) uses of inline-colon to associate the two things on either side. (Without nearby brackets, the scope of the colon or arrow is more easily taken to be the whole line.) expr1 except (expr2: expr3, expr4: expr5) expr1 except (expr2 -> expr3, expr4 -> expr5)
Notably, the "say it like you would in English" that convinced Perl still applies: "if" *without* a "then" is normally an extra condition added after the main point:
Normally ham, but fish if it's a Friday.
That's not how Python words ternary if, though.
Agreed ... the "say it like you would in English" applies only to the "expr if expr" form (proposed here and) used by comprehensions: [1/x for x in data if x]
value = expr except (Exception [as e]: default)
(and the similar but unmentioned)
value = expr except (Exception [as e] -> default)
The parenthesizing question and the choice of tokens are considered independent, so not all the cross-multiplications are listed.
The mapping analogy for ":" is good -- and is the reason to place parentheses there, as opposed to around the whole expression. Your preferred form -- without the internal parentheses -- looks very much like a suite-introduction, and not at all like the uses where an inline colon is acceptable.
I have some notes on that down the bottom:
http://www.python.org/dev/peps/pep-0463/#colons-always-introduce-suites
I know that they don't always introduce suites. I can't speak to the lambda precedent, but I do know that I personally often stumble when trying to parse it, so I don't consider it a good model. The other three inline uses (dict display, slide notation, and function parameter annotation) are effectively conjunction operators, saying that expr1 and expr2 are bound more tightly than you would assume if they were separated by commas. They only occur inside a fairly close bracket (of some sort), and if the bracket isn't *very* close, then there are usually multiple associates-colons inside the same bracket. data[3:5] data[-1:-3:-1] def myfunc(a:int=5, b:str="Jim", c:float=3.14) {'b': 2, 'c': 3, 'a': 1} With parentheses after the except, the except expression will match this pattern too -- particularly if there are multiple types of exception treated differently. expr1 except (expr2: expr3) Without (preferably internal) parentheses, it will instead look like a long line with a colon near the end, and a short continuation suite that got moved up a line because it was only one statement long. def nullfunc(self, a): pass expr1 except expr3: expr2
value = expr except Exception [as e] -> default
Without parens to group Exception and default, this looks too much like an annotation describing what the expr should return.
Can expressions have annotations?
Not yet. I have seen proposals. Other syntax (notably, Decorations) have expanded their domain. I would be mildly surprised if there isn't already a 3rd party template system using -> in places where it isn't currently defined.
All forms involving the 'as' capturing clause have been deferred ...
Nick is right that you should specify whether it is deferred or rejected, because the simplest implementation may lock you into too broad a scope if it is added later.
Put it this way: It's deferred until there's a non-closure means of creating a sub-scope.
The problem is that once it is deployed as leaking into the parent scope, backwards compatibility may force it to always leak into the parent scope. (You could document the leakage as a bug or as implementation-defined, but ... those choices are also sub-optimal.)
The four forms most supported by this proposal are, in order::
value = (expr except Exception: default) value = (expr except Exception -> default)
If there are not parentheses after "except", it will be very tempting (and arguably correct) to (at least mentally) insert them around the first two clauses -- which are evaluated first. But that leaks into
value = (expr except Exception): default
...
You can put parens around true_expr or cond, no problem, but you can't put them around both:
value = (true_expr if cond) else false_expr SyntaxError: invalid syntax
The fact that short-circuiting will prevent false_expr from even being evaluated means that "(true_expr if cond)" is a useful mental approximation, even though "true_expr if cond" is already to the left. Commutivity and Associativity are pretty strong biases; testing order of execution for apparently parallel expressions (such as different arguments to the same function in the same call) is a source of trick questions. When associativity is violated, computation normally proceeds from the left, and any time you need to jump ahead to simplify something that isn't left-most, that special case needs to be signalled. For short enough jumps, order of operations can be sufficient (e=mc^2), but you'll still see plenty of programmers inserting redundant parentheses just to make things clear (or maybe just to avoid having to look up the order of operations). The replacement value and the exception that triggers it are clearly associated more closely with each other than either is to the primary expression. Therefore, they *should* be grouped more tightly. This is a proposal with no perfect syntax, but I don't really see any upside to *not* grouping the exception with its exceptional result. At the moment, I don't know how to spell that closer grouping without at least one of: Parentheses or brackets of some sort A line break/new suite: - kills the proposal An inline associates-colon: - needs brackets anyhow, or it will be mistaken for a suite-introduction-colon. A new grouping operator, such as "->" - risk that it will be misinterpreted Relying on convention and taste: (main_expr except exc_expr fallback_expr) -jJ -- If there are still threading problems with my replies, please email me with details, so that I can try to resolve them. -jJ