I have been struggling to justify the need based on what I have read.  I hope this isn't a dupe, I only saw caching mentioned in passing.

Also please excuse some of the naive generalizations below for illustrative purposes.

Is there a reason memoization doesn't work? If f is truly expensive, using this syntax only makes sense if you have few comprehensions (as opposed to many iterations) and few other calls to f. Calling f in 10 comprehensions would (naively) benefit from memoization more than this.  It appears to me to be ad-hoc memoization with limited scope.  is this a fair statement?  

From readability, the examples put forth have been to explain the advantage, with which I agree.  However, i do not believe this scales well.

[(foo(x,y) as g)*(bar(y) as i) + g*foo(x,a) +baz(g,i) for x... for y...]

That's 3 functions, 2 iterators, 3 calls saved ('a' is some constant just to trigger a new call on foo).  I'm not trying to show ugly statements can be constructed, but show how quickly in _n iterators and _m functions readability declines.

it's seems the utility is bounded by f being not complex/costly enough for memoization, and ( _m, _n) being small enough to pass code review. The syntax does not create a readability issue, but by adding symbols, exacerbates an inherent issue with 'complex' comprehensions.

If I have understood, does this bounding on f, _m, and _n yield a tool with sufficient applicability for a language change?   I know it's already spoken about as an edge case, I'm just not clear on the bounds of that.  I am not against it, I just haven't seen this addressed. 

Thank you for putting the PEP together.  Again, I don't want to sound negative on it, I may have misunderstood wildly.  I like the idea conceptually, and I don't think it's anymore confusing to me than comprehensions were when I first encountered them, and it will yield a 'cool!' statement much like they did when I learned them.  


On Tue, Feb 27, 2018, 22:55 Gregory P. Smith <greg@krypto.org> wrote:

On Tue, Feb 27, 2018 at 2:35 PM Chris Angelico <rosuav@gmail.com> wrote:
This is a suggestion that comes up periodically here or on python-dev.
This proposal introduces a way to bind a temporary name to the value
of an expression, which can then be used elsewhere in the current
statement.

The nicely-rendered version will be visible here shortly:

https://www.python.org/dev/peps/pep-0572/

ChrisA

PEP: 572
Title: Syntax for Statement-Local Name Bindings
Author: Chris Angelico <rosuav@gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 28-Feb-2018
Python-Version: 3.8
Post-History: 28-Feb-2018


Abstract
========

Programming is all about reusing code rather than duplicating it.  When
an expression needs to be used twice in quick succession but never again,
it is convenient to assign it to a temporary name with very small scope.
By permitting name bindings to exist within a single statement only, we
make this both convenient and safe against collisions.


Rationale
=========

When an expression is used multiple times in a list comprehension, there
are currently several suboptimal ways to spell this, and no truly good
ways. A statement-local name allows any expression to be temporarily
captured and then used multiple times.


Syntax and semantics
====================

In any context where arbitrary Python expressions can be used, a named
expression can appear. This must be parenthesized for clarity, and is of
the form `(expr as NAME)` where `expr` is any valid Python expression,
and `NAME` is a simple name.

The value of such a named expression is the same as the incorporated
expression, with the additional side-effect that NAME is bound to that
value for the remainder of the current statement.

Just as function-local names shadow global names for the scope of the
function, statement-local names shadow other names for that statement.
They can also shadow each other, though actually doing this should be
strongly discouraged in style guides.


Example usage
=============

These list comprehensions are all approximately equivalent::

    # Calling the function twice
    stuff = [[f(x), f(x)] for x in range(5)]

    # Helper function
    def pair(value): return [value, value]
    stuff = [pair(f(x)) for x in range(5)]

    # Inline helper function
    stuff = [(lambda v: [v,v])(f(x)) for x in range(5)]

    # Extra 'for' loop - see also Serhiy's optimization
    stuff = [[y, y] for x in range(5) for y in [f(x)]]

    # Expanding the comprehension into a loop
    stuff = []
    for x in range(5):
        y = f(x)
stuff.append([y, y])

    # Using a statement-local name
    stuff = [[(f(x) as y), y] for x in range(5)]

If calling `f(x)` is expensive or has side effects, the clean operation of
the list comprehension gets muddled. Using a short-duration name binding
retains the simplicity; while the extra `for` loop does achieve this, it
does so at the cost of dividing the expression visually, putting the named
part at the end of the comprehension instead of the beginning.

Statement-local name bindings can be used in any context, but should be
avoided where regular assignment can be used, just as `lambda` should be
avoided when `def` is an option.


Open questions
==============

1. What happens if the name has already been used? `(x, (1 as x), x)`
   Currently, prior usage functions as if the named expression did not
   exist (following the usual lookup rules); the new name binding will
   shadow the other name from the point where it is evaluated until the
   end of the statement.  Is this acceptable?  Should it raise a syntax
   error or warning?

2. The current implementation [1] implements statement-local names using
   a special (and mostly-invisible) name mangling.  This works perfectly
   inside functions (including list comprehensions), but not at top
   level.  Is this a serious limitation?  Is it confusing?

3. The interaction with locals() is currently[1] slightly buggy.  Should
   statement-local names appear in locals() while they are active (and
   shadow any other names from the same function), or should they simply
   not appear?

4. Syntactic confusion in `except` statements.  While technically
   unambiguous, it is potentially confusing to humans.  In Python 3.7,
   parenthesizing `except (Exception as e):` is illegal, and there is no
   reason to capture the exception type (as opposed to the exception
   instance, as is done by the regular syntax).  Should this be made
   outright illegal, to prevent confusion?  Can it be left to linters?

5. Similar confusion in `with` statements, with the difference that there
   is good reason to capture the result of an expression, and it is also
   very common for `__enter__` methods to return `self`.  In many cases,
   `with expr as name:` will do the same thing as `with (expr as name):`,
   adding to the confusion.


References
==========

.. [1] Proof of concept / reference implementation
   (https://github.com/Rosuav/cpython/tree/statement-local-variables)


-1 today

My first concern for this proposal is that it gives parenthesis a non-intuitive meaning.  ()s used to be innocuous.  A way to group things, make explicit the desired order of operations, and allow spanning across multiple lines.

With this proposal, ()s occasionally take on a new meaning to cause additional action to happen _outside_ of the ()s - an expression length name binding.

Does this parse?

  print(fetch_the_comfy_chair(cardinal) as chair, "==", chair)

SyntaxError?  My read of the proposal suggests that this would be required:

  print((fetch_the_comfy_chair(cardinal) as chair), "==", chair)

But that sets off my "excess unnecessary parenthesis" radar as this is a new need it isn't trained for.  I could retrain the radar...

I see people try to cram too much functionality into one liners.  By the time you need to refer to a side effect value more than once in a single statement... Just use multiple statements.  It is more clear and easier to debug.

Anything else warps human minds trying to read, understand, review, and maintain the code.  {see_also: "nested list comprehensions"}

'''insert favorite Zen of Python quotes here'''

We've got a whitespace delimited language. That is a feature. Lets keep it that way rather than adding a form of (non-curly) braces.

I do understand the desire.  So far I remain unconvinced of a need. 

Practical examples from existing code that become clearly easier to understand afterwards instead of made the examples with one letter names may help.

2cents,
-gps
 
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/