[Python-Dev] Why not make frames? [was: Alternative forms [was: PEP 463: Exception-catching expressions]]
Jim J. Jewett
jimjjewett at gmail.com
Mon Mar 10 03:16:38 CET 2014
TL;DR:
expr except (default if exc_expr)
expr (except default if exc_expr)
expr except (exc_expr: default)
expr (except exc_expr: default)
(1) Group the exceptions with the default they imply.
(2) inline-":" still needs () or [] or {}.
(3) Consider the expression inside a longer line.
(3a) Does the except expression need to be general, or would it work
if it were limited to a subclause of variable assignments?
(3b) What about comprehensions?
On Fri Mar 7 20:54:31 CET 2014, Chris Angelico wrote:
>On Sat, Mar 8, 2014 at 5:58 AM, Jim J. Jewett <jimjjewett at gmail.com> wrote:
>> (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. ]
...
> I don't see except expressions as fundamentally more associated with
> if/else than with, say, an or chain, which works left to right.
I do, because of the skipping portion.
Short-circuiting operators, such as an "or" chain, never skip a clause
unless they are skipping *every* subsequent clause.
An "if" statement sometimes skips the (unlabeled in python) "then"
clause, but still processes the even-later "else" clause.
A "try" statement sometimes skips the remainder of the try suite but
still executes the later subordinate "except" and "finally" clauses.
Note that this only explains why I see "except" as more closely related
to "if" than to "or"; it isn't sufficient to justify going back to
execute the skipped clause later. That said, going back to a previous
location is a lot easier to excuse after an error handler than in
"regular" code.
> Analysis of the Python standard library suggests that the single-if
> situation is *by far* the most common, to the extent that it'd hardly
> impact the stdlib at all to add multiple except clauses to the
> proposal. Do you have a strong use-case for the more full syntax?
I do not.
I dislike the arbitrary restriction, and I worry that lifting it later
(while maintaining backwards compatibility) will result in a syntax
wart, but I do not have a compelling use case for that later relaxation.
>> and I strongly prefer that they [the parentheses] be internal
>> (which you fear looks too much like calling a function named except).
>> In that case, it is:
>> expr1 except (expr3 if expr2)
> I'm still not really seeing how this is better.
For one thing, it makes it clear that the "if" keyword may be messing
with the order of evaluation.
I don't claim that syntax is perfect. I do think it is less flawed
than the no-parentheses (or external parentheses) versions:
(expr1 except expr3 if expr2)
expr1 except expr3 if expr2
because the tigher parentheses correctly indicate that expr2 and expr3
should be considered as a (what-to-do-in-case-of-error) group, which
interacts (as a single unit) with the main expression.
I also think it is (very slighly) better than the
colon+internal-parentheses version:
expr1 except (expr2: expr3)
which in turn is far, far better than the colon versions with external
or missing parentheses:
(expr1 except expr2: expr3)
expr1 except expr2: expr3
because I cannot imagine reading an embedded version of either of those
without having to mentally re-parse at the colon. An example assuming
a precedence level that may not be what the PEP proposes:
if myfunc(5, expr1 except expr2: expr3, "label"):
for i in range(3, 3*max(data) except TypeError: 9, 3):
...
if myfunc(5, (expr1 except expr2: expr3), "label"):
for i in range(3, (3*max(data) except TypeError: 9), 3):
...
if myfunc(5, expr1 except (expr2: expr3), "label"):
for i in range(3, 3*max(data) except (TypeError: 9), 3):
...
if myfunc(5, expr1 except (expr2: expr3), "label"):
for i in range(3, 3*max(data) (except TypeError: 9), 3):
...
if myfunc(5, expr1 except (expr3 if expr3), "label"):
for i in range(3, 3*max(data) (except 9 if TypeError), 3):
...
if myfunc(5, expr1 except (expr3 if expr3), "label"):
for i in range(3, 3*max(data) except (9 if TypeError), 3):
myarg = expr1 except (expr3 if expr2)
if myfunc(5, myarg, "label"):
limit = 3*max(data) except (9 if TypeError)
for i in range(3, limit, 3):
Yes, I would prefer to create a variable naming those expressions,
but these are all still simple enough that I would expect to have
to read them. (I like constructions that get ugly just a bit faster
than they get hard to understand.) If I have to parse any of them,
the ones at the bottom are less difficult than the ones at the top.
> With the colon version, it looks very much like dict display,
which is good, since that is one of the acceptable uses of inline-colon.
> only with different brackets around it; in some fonts, that'll be
> very easily confused.
I've had more trouble with comma vs period than with different types
of bracket. But let's assume that there is confusion, and someone sees:
expr1 except [expr2:expr3]
or
expr1 except {expr2:expr3}
These are not yet defined any more than the tuple-with-colon version is,
nor do they have an obvious-yet-incompatible meaning. In fact, I would
prefer either of them to any version that does not syntactically
associate the exception list with the result those exceptions imply.
>> 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.
> Not sure that really helps. This isn't going to be as tight as a
> slice, and it's most often not going to have multiple colons inside
> the brackets.
The colon is acceptable only to the extent that this similarity does
help. So if a colon is used, I want the similarity to be as strong
as possible -- which, I suppose, is another argument for tightening
the parentheses, and possibly an argument for using [] instead of ().
-jJ
--
If there are still threading problems with my replies, please
email me with details, so that I can try to resolve them. -jJ
More information about the Python-Dev
mailing list