[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