[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] Informal educator feedback on PEP 572 (was Re: 2018 Python Language Summit coverage, last part)

On Sat, Jun 30, 2018 at 11:32:03PM -0500, Tim Peters wrote:

> So, no, gaining performance is _not_ the motivation here.  You already had
> a way to make it "run fast'.  The motivation is the _brevity_ assignment
> expressions allow while _retaining_ all of the two-statement form's
> advantages in easier readability, easier reasoning, reduced redundancy, and
> performance.

I think the two of you (Tim and Nick) are in violent agreement :-)

In fairness to Nick performance is _a_ motivation here, in the sense of 
"how can we avoid making redundant multiple calls to a function in an 
expression without splitting it into multiple statements?".

If performance were the *only* concern, then I agree with your point 
that we already have a solution. Simply refactor this:

    process(expensive_call(arg), expensive_call(arg) + 1)

to this:

    useful_value = expensive_call(arg)
    process(useful_value, useful_value + 1)

But since performance is not the *only* concern, all the other factors 
you refer to (saving vertical space, DRY, side-effects, etc) count too.

Assignment expressions aren't valuable because they give us One Big Win. 
They're valuable because they give us Many Little Wins, which we 
(proponents of PEP 572) believe will outweigh the (minor) additional 
costs in complexity and opportunities for abuse.

The closest (in my opinion) assignment expressions comes to a One Big 
Win is to reduce the need for cascades of if... statements:

    m = re.match(pattern, text)
    if m:
        m = re.match(other_pattern, text)
        if m:
            m = re.match(third_pattern, text)
            if m: ...

which wastes both vertical and horizontal space.

If that were the *only* win, I'd consider dedicated syntax for if/elif 
statements alone, but it isn't. Little wins include:

- use in while statement headers, "while x:= expr"

- DRY inside comprehensions, as an alternative to the neat but somewhat
  obscure idiom:

    [(x, x+1, x*2) for a in it for x in (func(a),) if x]

- running totals and similar inside comprehensions, where
  we need a way to initialise the total on the first iteration

    total = 0
    [total := total + a for a in it]

- and examples such as Tim's use of Heron's Formula:

    area = sqrt((s := (a+b+c)/2)*(s-a)*(s-b)*(s-c))

This last one is, I think, the only one where the postfix form 
reads better, but maybe I only say that because I'm more 
familiar with it:

    area = sqrt(s*(s-a)*(s-b)*(s-c)) where s = (a+b+c)/2