On 08/08/2020 02:16, Steven D'Aprano wrote:
On Fri, Aug 07, 2020 at 06:40:33PM +0100, Rob Cliffe via Python-ideas wrote:

On 07/08/2020 17:16, Serhiy Storchaka wrote:
The main problem to me with the exception catching expression is that
you need to add the exception name and several keywords, and it makes
real-world examples too long, so you will need to split the expression
on several lines, and add extra parenthesis. And in this case there is
no much difference between expression

     x = (
         <long expression>
        except LongExceptionName:
         <other long expression>
     )

and statement

     try:
         x = <long expression>
     except LongExceptionName:
         x = <other long expression>

(actually the statement may be shorter and better aligned).
Serhiy is correct that some examples will be long and cumbersome, but
many other expressions are long and cumbersome, and we leave it up to
the writer of the code to decide whether to extend a single expression
over multiple lines or split it into multiple statements using temporary
variables.

In Serhiy's example above, it is up to the coder and their aesthetic
sense to decide whether to use the expression form or the statement
form, just as today they can decide between:


     if <very long condition>:
         x = <long expression>
     else:
         x = <another long expression>


and


     x = (<long expression>
          if <very long condition>
          else <another long expression>
          )


Neither is necessarily better or worse than the other, it will depend on
the surrounding code, whether the value is being bound to a variable or
being immediately used in another expression:

     function(arg,
              another_arg,
              spam=long_conditional_or_except_expression,
              eggs=expression
              )


Just like if statements vs ternary if expressions, the writer will get
to choose which is better for their purposes, and whether or not to use
temporary variables.


This is a strawman argument.  You set up a case where exception-catching
expressions are poor (long expression, long exception name) and then
knock it down.
Please don't misuse "strawman argument" in this way. Serhiy is making a
legitimate criticism of the feature, and explicitly labelling it as a a
reason he personally does not like the feature.

It is not a strawman to point out your personal reasons for disliking a
feature. This is a legitimate issue with the suggested syntax: it is not
especially terse, and if the expressions and exceptions are long, as
they sometimes will be, the whole thing will be little better, or not
better at all, than using a try...except statement.
Yes but Serhiy implied that the whole thing will *always* be too long, which won't be true.  He said, without qualification, "it makes real-world examples too long".

You don't have to agree with Serhiy's preference to recognise that there
are cases where this proposal will save no lines of code. And probably
not rare cases -- I expect that they will be much more common than the
short examples you quote:

If you read the PEP you will find plenty of short examples:

        process(dic[key] except KeyError: None)
        value = (lst[2] except IndexError: "No value")
        cond = (args[1] except IndexError: None)
        pwd = (os.getcwd() except OSError: None)
        e.widget = (self._nametowidget(W) except KeyError: W)
        line = (readline() except StopIteration: '')
etc.
The first is a poor example because that can so easily be written as:

     process(dic.get(key))

The second and third are basically the same example.

The fourth example of os.getcwd is excellent. But the fifth example
strikes me as exceedingly weak. You are calling a private method, which
presumably means you wrote the method yourself. (Otherwise you have no
business calling the private method of a class you don't control.) So
why not just add an API for providing a default instead of raising?

And the final example is also weak. If readline is a bound method of a
file object:

     readline = open(path, 'r').readline

then it already returns the empty string at the end of file. And if it
isn't, then perhaps it should, rather than raising a completely
inappropriate StopIteration exception.
I just copied the first few examples from the PEP without analysing them, so you are criticising the PEP, not me.  I hope Chris Angelico appreciates the criticism (I mean that seriously, not as a piece of sarcasm).

Other problem specific to the PEP 463 syntax is using colon in
expression. Colon is mainly used before indented block in complex
statements. It was introduced in this role purely for aesthetic reasons.
I disagree with Serhiy here: I believe that there is objective evidence
in the form of readability studies which suggest that colons before
indented blocks aid readability, although I have lost the reference.


Using it in other context is very limited (dict display, lambda,
annotations, what else?).

Slice notation.  As you would have discovered if you read the PEP.

No need to be so catty over a minor bit of forgetfulness, I'm sure that
Serhiy has read the PEP, and we know that Serhiy is experienced enough
to have seen slice notation a few times. Let's not condemn him for a
momentary bit of forgetfulness.
You're right.

*I apologise, Serhiy.*


Dict display and slices are hardly of "very limited" use.  (I can't
comment on annotations, I don't use them.)
I don't believe that Serhiy is referring to how frequently slices or
dicts are used, but to the *very limited number of contexts* where
Python uses colons:

- to introduce an indented block
- dict displays {key: value}
- slices obj[2:7]
- lambda arg: expression
- annotations def spam(arg:int)

So I think that there are only five places where colons are used, and
only three of them are part of expressions. Out of the dozens of
syntactic forms in Python, I think that's quite limited.
Surely if you are going to argue against a colon in the middle of a line on the grounds that that is unexpected/jarring/whatever, what matters is how often we see it in code, not how many categories it fits into.  I did a quick token analysis of the 3.8.3 stdlib and found that approximately 80% of colons introduced a suite, 20% didn't.  But FWIW there are AFAIK 10 keywords that introduce a suite (def, elif, else, except, finally, for, if, try, while, with), or alternatively 6 constructs that use suites (for, while, if..., try..., def, with). Whichever way you cut it (80% to 20%, or 8 to 4, or 6 to 4), a majority of colons do introduce a suite, but not an overwhelming majority.  It is not true that "colons almost always introduce a suite" or "Using it in other contexts is very limited".

(But not as limited as I would have guessed ahead of time.)



_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UDPSTWTSPFSCWQR5SSUJFLH7KQIHO3HT/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to