[Python-Dev] Alternative forms [was: PEP 463: Exception-catching expressions]

Chris Angelico rosuav at gmail.com
Thu Mar 6 23:26:47 CET 2014


On Fri, Mar 7, 2014 at 7:29 AM, Jim J. Jewett <jimjjewett at gmail.com> wrote:
>
> The PEP currently says:
>
>> Alternative Proposals
>> =====================
>
>> Discussion on python-ideas brought up the following syntax suggestions::
>
>>    value = expr except default if Exception [as e]
>
> This one was rejected because of the out-of-order evaluation.
>
> Note, however, that the (farthest left) expr is always evaluated first;
> the only out-of-order evaluation is "default if Exception".
>
> "default if Exception" is precisely the same evaluation order
> (clause after the "if" skips ahead of the clause before the "if")
> as in the existing if-expression, and the existing if-filters in
> comprehensions.

Yes, but that's still out of order.

> The same justifications for that order violation generally apply here too.
> You can argue that they weren't sufficient justification in the first
> place, but that is water under the bridge; *re*-using out-of-order-"if"
> shouldn't add any additional costs.
>
> [Err... unless people take the "if" too literally, and treat the
> Exception clause as a boolean value, instead of as an argument to the
> "except" keyword.]

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. (Style guides should, of course,
decry this as unreadable, but both the machine parser and any humans
reading the code have to assume style guides mightn't be followed.)

> 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 of "if", both in its statement form and as part of "true if
expr else false", is that it is followed by something that's evaluated
as boolean; and all exception types and tuples will be true in that
context.

> 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. "Ham, if it's not
Friday; otherwise fish" is closer, or inverting that: "Fish on
Fridays, but normally ham". English is pretty flexible with how you
lay things out :)

>>    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

>>    value = expr except Exception [as e] continue with default
>
> This one works for me, but only because I read "continue with" as a
> compound keyword.  I assume the parser would too.  :D  But I do
> recognize that it is a poor choice for those who see the space as a
> more solid barrier.

That depends on all parsers (computer and human) being okay with the
two-keyword unit. Would have to poll human parsers to see how they
feel about it.

>>    value = expr except(Exception) default # Catches only the named type(s)
>
> This looks too much like the pre-"as" way of capturing an exception.

Not sure what the connection is, but I don't like the style anyway:
putting an expression immediately after a close parenthesis seems odd
(I can't think of anything else in Python that has that).

>>    value = default if expr raise Exception
>
> (Without new keyword "raises",) I would have to work really hard not to
> read that as:
>
>     __temp = default
>     if expr:
>         raise Exception
>     value = __temp

Yeah; on the flip side, "raises" doesn't add a huge amount of clarity,
and it's creating a new keyword that's not identical, but so all-but -
oh Saphir, is it not quite too "all-but"?

>>    value = expr or else default if Exception
>
> To me, this just seems like a wordier and more awkward version of
>
>     expr except (default if Exception [as e])
>
> including the implicit parentheses around "default if Exception".

And mainly, it abuses three keywords that can all already exist in an
expression, and doesn't use either "try" or "except". Suppose you saw
that, and wanted to know what it does. What would you search the docs
for?

>>    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?

>> All forms involving the 'as' capturing clause have been deferred from
>> this proposal in the interests of simplicity, but are preserved in the
>> table above as an accurate record of suggestions.
>
> 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. If that never happens, then this is postponed
indefinitely (like pay-day for the Dormouse's good workers); and that
doesn't majorly bother me, because the "as" clause isn't very much
used in the simple try/except that want to be made into expressions.
Philosophically it's a nice thing to support, so it's not Rejected;
but it's dependent on a feature that CPython doesn't have, and may
never have. In theory, someone could implement subscopes in a
different Python, and then reopen this part of the proposal as
"CPython won't support it, but MyPy will, can we make this official
please?"; I don't know that anyone would bother, but it'd be plausible
with the syntax as given.

>> 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
>
> which strongly resembles the suite-starter ":", but has very little
> in common with the mapping ":" or the signature ":".
>
>     value = (expr except Exception) -> default
>
> which looks like an annotation, rather than a part of the value-determination.

value = true_expr if cond else false_expr

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

Why would you want to put them around the first two expressions here?
It's exactly the same: you have a ternary operator, so you can't
parenthesize two of its terms and one of its keywords. That breaks up
the parse unit, visually and logically.

So that simply wouldn't work. :)

ChrisA


More information about the Python-Dev mailing list