<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p>I hope nobody will mind too much if I throw in my (relatively
      uninformed) 2c before some of the big guns respond.</p>
    <p>First: Well done, Chris, for all the work on this.  IMHO this
      could be a useful Python enhancement (and reduce the newsgroup
      churn <span class="moz-smiley-s1"><span>:-)</span></span>).<br>
    </p>
    On 27/02/2018 22:27, Chris Angelico wrote:<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">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:

<a class="moz-txt-link-freetext" href="https://www.python.org/dev/peps/pep-0572/">https://www.python.org/dev/peps/pep-0572/</a>

ChrisA

PEP: 572
Title: Syntax for Statement-Local Name Bindings
Author: Chris Angelico <a class="moz-txt-link-rfc2396E" href="mailto:rosuav@gmail.com"><rosuav@gmail.com></a>
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.</pre>
    </blockquote>
    It may be pedantic of me (and it <b>will</b> produce a more
    pedantic-sounding sentence) but I honestly think that<br>
    "safe against name collisions" is clearer than "safe against
    collisions", and that clarity matters.<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">


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.</pre>
    </blockquote>
    IMHO the first sentence is a bit of an overstatement (though of
    course it's a big part of the PEP's "sell").  <br>
    How about "... there are currently several ways to spell this, none
    of them ideal."<br>
    <br>
    Also, given that a list comprehension is an expression, which in
    turn could be part of a larger expression, would it be appropriate
    to replace "expression" by "sub-expression" in the 2 places where it
    occurs in the above paragraph?<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">


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

In any context where arbitrary Python expressions can be used, a named
expression can appear. This must be parenthesized for clarity,</pre>
    </blockquote>
    I agree, pro tem (not that I am claiming that my opinion counts for
    much).  I'm personally somewhat allergic to making parentheses
    mandatory where they really don't need to be, but trying to think
    about where they could be unambiguously omitted makes my head spin. 
    At least, if we run with this for now, then making them
    non-mandatory in some contexts, at some future time, won't lead to
    backwards incompatibility.<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap=""> 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</pre>
    </blockquote>
                # Calling the function twice (assuming that side effects
    can be ignored)<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">
    stuff = [[f(x), f(x)] for x in range(5)]

    # Helper function</pre>
    </blockquote>
                # External helper function<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">
    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])</pre>
    </blockquote>
    Please feel free to ignore this, but (trying to improve on the above
    example):<br>
                # Using a generator:<br>
                def gen():<br>
                    for x in range(5):<br>
                        y = f(x)<br>
                        yield y,y<br>
                stuff = list(gen())<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">

    # 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.</pre>
    </blockquote>
    Maybe add to last sentence "and of adding (at least conceptually)
    extra steps: building a 1-element list, then extracting the first
    element"<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">

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?</pre>
    </blockquote>
    IMHO this is not only acceptable, but the (only) correct behaviour. 
    Your crystal-clear statement "the new name binding will shadow the
    other name from the point where it is evaluated until the end of the
    statement
    " is <b>critical</b> and IMO what should happen.<br>
    Perhaps an extra example or two, to clarify that *execution order*
    is what matters, might help, e.g.<br>
        y if (f() as y) > 0 else None<br>
    will work as expected, because "(f() as y)" is evaluated before the
    initial "y" is (if it is).<br>
    <br>
    [Parenthetical comment: Advanced use of this new feature would
    require knowledge of Python's evaluation order.  But this is not an
    argument against the PEP, because the same could be said about
    almost any feature of Python, e.g.<br>
        [ f(x), f(x) ]<br>
    where evaluating f(x) has side effects.]<br>
    <br>
    2. The current implementation [1] implements statement-local names
    using
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">   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?</pre>
    </blockquote>
    It's great that it works perfectly in functions and list
    comprehensions, but it sounds as if, at top level, in rare
    circumstances it could produce a hard-to-track-down bug, which is
    not exactly desirable.  It's hard to say more without knowing more
    details.  As a stab in the dark, is it possible to avoid it by
    including the module name in the mangling?  Sorry if I'm talking
    rubbish.<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">

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?</pre>
    </blockquote>
    IMHO this is an implementation detail.  IMO you should have some
    idea what you're doing when you use locals().  But I think
    consistency matters - either the temporary variable *always* gets
    into locals() "from the point where it is evaluated until the end of
    the statement", or it *never* gets into locals().  (Possibly the
    language spec should specify one or the other - I'm not sure, time
    may tell.)<br>
    <blockquote type="cite"
cite="mid:CAPTjJmov_ZS6aCG457jMdZdFW4Qzi6yQwXnPtZjUVxRrQr0hVQ@mail.gmail.com">
      <pre wrap="">

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.</pre>
    </blockquote>
    This (4. and 5.) shows that we are using "as" in more than one
    sense, and in a perfect world we would use different keywords.  But
    IMHO (admittedly, without having thought about it much) this isn't
    much of a problem.  Again, perhaps some clarifying examples would
    help.<br>
    <br>
    Some pedantry: <br>
    <br>
        One issue not so far explicitly mentioned: IMHO it should be
    perfectly legal to assign a value to a temporary variable, and then
    not use that temporary variable (just as it is legal to assign to a
    variable in a regular assignment statement, and then not use that
    variable) though linters should IMO point it out.  E.g. you might
    want to modify (perhaps only temporarily)<br>
            a = [ (f() as b), b ]<br>
    to<br>
            a = [ (f() as b), c ]<br>
    <br>
    Also (and I'm relying on "In any context where arbitrary Python
    expressions can be used, a named expression can appear."
    ),<br>
    linters should also IMO point to<br>
        a = (42 as b)<br>
    which AFAICT is a laborious synonym for<br>
        a = 42<br>
    <br>
    And here's a thought: What are the semantics of<br>
        a = (42 as a) # Of course a linter should point this out too<br>
    At first I thought this was also a laborious synonym for "a=42". 
    But then I re-read your statement (the one I described above as
    crystal-clear) and realised that its exact wording was even more
    critical than I had thought:<br>
        "the new name binding will shadow the other name from the point
    where it is evaluated until the end of the statement"<br>
    Note: "until the end of the <b>statement</b>".  NOT "until the end
    of the <b>expression</b>".  The distinction matters.<br>
    If we take this as gospel, all this will do is create a temporary
    variable "a", assign the value 42 to it twice, then discard it. 
    I.e. it effectively does nothing, slowly.<br>
    Have I understood correctly?  Very likely you have considered this
    and mean exactly what you say, but I am sure you will understand
    that I mean no offence by querying it.<br>
    <br>
    Best wishes<br>
    Rob Cliffe<br>
    <br>
  </body>
</html>