<div dir="auto">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.<div dir="auto"><br></div><div dir="auto">Also please excuse some of the naive generalizations below for illustrative purposes.</div><div dir="auto"><br></div><div dir="auto">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?  </div><div dir="auto"><br></div><div dir="auto">From readability, the examples put forth have been to explain the advantage, with which I agree.  However, i do not believe this scales well.</div><div dir="auto"><br></div><div dir="auto">[(foo(x,y) as g)*(bar(y) as i) + g*foo(x,a) +baz(g,i) for x... for y...]</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto"><span style="font-family:sans-serif">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. </span><br></div><div dir="auto"><br></div><div dir="auto">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.  </div><div dir="auto"><br></div></div><br><div class="gmail_quote"><div dir="ltr">On Tue, Feb 27, 2018, 22:55 Gregory P. Smith <<a href="mailto:greg@krypto.org">greg@krypto.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><br></div><div><div class="gmail_quote"><div dir="ltr">On Tue, Feb 27, 2018 at 2:35 PM Chris Angelico <<a href="mailto:rosuav@gmail.com" target="_blank" rel="noreferrer">rosuav@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">This is a suggestion that comes up periodically here or on python-dev.<br>
This proposal introduces a way to bind a temporary name to the value<br>
of an expression, which can then be used elsewhere in the current<br>
statement.<br>
<br>
The nicely-rendered version will be visible here shortly:<br>
<br>
<a href="https://www.python.org/dev/peps/pep-0572/" rel="noreferrer noreferrer" target="_blank">https://www.python.org/dev/peps/pep-0572/</a><br>
<br>
ChrisA<br>
<br>
PEP: 572<br>
Title: Syntax for Statement-Local Name Bindings<br>
Author: Chris Angelico <<a href="mailto:rosuav@gmail.com" target="_blank" rel="noreferrer">rosuav@gmail.com</a>><br>
Status: Draft<br>
Type: Standards Track<br>
Content-Type: text/x-rst<br>
Created: 28-Feb-2018<br>
Python-Version: 3.8<br>
Post-History: 28-Feb-2018<br>
<br>
<br>
Abstract<br>
========<br>
<br>
Programming is all about reusing code rather than duplicating it.  When<br>
an expression needs to be used twice in quick succession but never again,<br>
it is convenient to assign it to a temporary name with very small scope.<br>
By permitting name bindings to exist within a single statement only, we<br>
make this both convenient and safe against collisions.<br>
<br>
<br>
Rationale<br>
=========<br>
<br>
When an expression is used multiple times in a list comprehension, there<br>
are currently several suboptimal ways to spell this, and no truly good<br>
ways. A statement-local name allows any expression to be temporarily<br>
captured and then used multiple times.<br>
<br>
<br>
Syntax and semantics<br>
====================<br>
<br>
In any context where arbitrary Python expressions can be used, a named<br>
expression can appear. This must be parenthesized for clarity, and is of<br>
the form `(expr as NAME)` where `expr` is any valid Python expression,<br>
and `NAME` is a simple name.<br>
<br>
The value of such a named expression is the same as the incorporated<br>
expression, with the additional side-effect that NAME is bound to that<br>
value for the remainder of the current statement.<br>
<br>
Just as function-local names shadow global names for the scope of the<br>
function, statement-local names shadow other names for that statement.<br>
They can also shadow each other, though actually doing this should be<br>
strongly discouraged in style guides.<br>
<br>
<br>
Example usage<br>
=============<br>
<br>
These list comprehensions are all approximately equivalent::<br>
<br>
    # Calling the function twice<br>
    stuff = [[f(x), f(x)] for x in range(5)]<br>
<br>
    # Helper function<br>
    def pair(value): return [value, value]<br>
    stuff = [pair(f(x)) for x in range(5)]<br>
<br>
    # Inline helper function<br>
    stuff = [(lambda v: [v,v])(f(x)) for x in range(5)]<br>
<br>
    # Extra 'for' loop - see also Serhiy's optimization<br>
    stuff = [[y, y] for x in range(5) for y in [f(x)]]<br>
<br>
    # Expanding the comprehension into a loop<br>
    stuff = []<br>
    for x in range(5):<br>
        y = f(x)<br>
stuff.append([y, y])<br>
<br>
    # Using a statement-local name<br>
    stuff = [[(f(x) as y), y] for x in range(5)]<br>
<br>
If calling `f(x)` is expensive or has side effects, the clean operation of<br>
the list comprehension gets muddled. Using a short-duration name binding<br>
retains the simplicity; while the extra `for` loop does achieve this, it<br>
does so at the cost of dividing the expression visually, putting the named<br>
part at the end of the comprehension instead of the beginning.<br>
<br>
Statement-local name bindings can be used in any context, but should be<br>
avoided where regular assignment can be used, just as `lambda` should be<br>
avoided when `def` is an option.<br>
<br>
<br>
Open questions<br>
==============<br>
<br>
1. What happens if the name has already been used? `(x, (1 as x), x)`<br>
   Currently, prior usage functions as if the named expression did not<br>
   exist (following the usual lookup rules); the new name binding will<br>
   shadow the other name from the point where it is evaluated until the<br>
   end of the statement.  Is this acceptable?  Should it raise a syntax<br>
   error or warning?<br>
<br>
2. The current implementation [1] implements statement-local names using<br>
   a special (and mostly-invisible) name mangling.  This works perfectly<br>
   inside functions (including list comprehensions), but not at top<br>
   level.  Is this a serious limitation?  Is it confusing?<br>
<br>
3. The interaction with locals() is currently[1] slightly buggy.  Should<br>
   statement-local names appear in locals() while they are active (and<br>
   shadow any other names from the same function), or should they simply<br>
   not appear?<br>
<br>
4. Syntactic confusion in `except` statements.  While technically<br>
   unambiguous, it is potentially confusing to humans.  In Python 3.7,<br>
   parenthesizing `except (Exception as e):` is illegal, and there is no<br>
   reason to capture the exception type (as opposed to the exception<br>
   instance, as is done by the regular syntax).  Should this be made<br>
   outright illegal, to prevent confusion?  Can it be left to linters?<br>
<br>
5. Similar confusion in `with` statements, with the difference that there<br>
   is good reason to capture the result of an expression, and it is also<br>
   very common for `__enter__` methods to return `self`.  In many cases,<br>
   `with expr as name:` will do the same thing as `with (expr as name):`,<br>
   adding to the confusion.<br>
<br>
<br>
References<br>
==========<br>
<br>
.. [1] Proof of concept / reference implementation<br>
   (<a href="https://github.com/Rosuav/cpython/tree/statement-local-variables" rel="noreferrer noreferrer" target="_blank">https://github.com/Rosuav/cpython/tree/statement-local-variables</a>)<br><br></blockquote><div><br></div><div>-1 today<div><br></div><div>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.<div><br></div><div>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.<br><div><br></div><div>Does this parse?</div><div><br></div><div>  print(fetch_the_comfy_chair(cardinal) as chair, "==", chair)</div><div><br></div><div>SyntaxError?  My read of the proposal suggests that this would be required:</div><div><br></div><div><div>  print((fetch_the_comfy_chair(cardinal) as chair), "==", chair)</div><br class="m_4052139929454909407inbox-inbox-inbox-inbox-Apple-interchange-newline"></div><div>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...</div><div><br></div><div>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.</div><div><br></div><div>Anything else warps human minds trying to read, understand, review, and maintain the code.  {see_also: "nested list comprehensions"}</div><div><br></div><div>'''insert favorite Zen of Python quotes here'''</div><div><br></div><div>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.<br></div><div><br></div><div>I do understand the desire.  So far I remain unconvinced of a need. </div><div><br></div><div>Practical examples from existing code that become clearly easier to understand afterwards instead of made the examples with one letter names may help.</div><div><br></div><div>2cents,</div><div>-gps</div></div></div></div><div> </div></div></div></div></div></div>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank" rel="noreferrer">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</blockquote></div>