[Python-ideas] PEP 572 version 2: Statement-Local Name Bindings

Nick Coghlan ncoghlan at gmail.com
Sun Mar 25 00:13:13 EDT 2018


On 25 March 2018 at 13:51, Chris Angelico <rosuav at gmail.com> wrote:

> On Sun, Mar 25, 2018 at 2:48 PM, Ethan Furman <ethan at stoneleaf.us> wrote:
> > On 03/24/2018 01:35 AM, Nick Coghlan wrote:
> >
> >> In comprehensions and generator expressions, we'd need to explain why
> >> inline assignments in the outermost iterator
> >> expression leak but those in filter expressions, inner iterator
> >> expressions, and result expressions don't.
> >
> >
> > I don't understand -- could you give an example?
> >
>
> Let's suppose we have assignment expressions. I'm going to use "(expr
> as name)" syntax for this example.
>
> a = [(1 as b) for c in (d as e) if (2 as f)]
>
> Which of these six names is local to the comprehension and which can
> leak? Due to the requirements of class scope, 'd' must be looked up in
> the outer scope. That means that its corresponding 'as e' must also
> land in the outer scope. 'a', of course, is in the outer scope. The
> other four are local to the inner function that implements the
> comprehension.
>

Adding qualifiers to the names to show which names would be resolved where:

    outer_a = [(1 as inner_b) for inner_c in (outer_d as outer_e) if (2 as
inner_f)]
    print(outer_a, outer_d, outer_e) # Works
    print (inner_b, inner_c, inner_f) # Name error

In current Python,with the outer scope's name bindings being read-only
inside a comprehension, this difference in scope resolution really only
matters at class scope, and when using the two-namespace form of exec and
eval (since method semantics result in that impacting which names the
comprehension can see). At function scope, referenced names from the outer
scope get resolved as closure references, while at module scope they get
resolved as global references, so you have to look at the generated
bytecode to see the differences.

That all changes once we start allowing name binding (in any form) at the
level of expressions rather than statements: regardless of the nature of
the outer scope, the exact structure of the implicit comprehension scope
becomes relevant whenever you bind names inside the comprehension.

Now, the notable *distinction* here relative to the old iteration variable
leakage is that doing a name binding in the outermost iterable expression
can always be refactored to use an ordinary assignment statement instead:

    outer_e = outer_d
    outer_a = [(1 as inner_b) for inner_c in outer_d if (2 as inner_f)]
    print(outer_a, outer_d, outer_e) # Works
    print (inner_b, inner_c, inner_f) # Name error

This formulation also makes it clear that "outer_e" will only resolve
inside the comprehension if "outer_d" already resolves.

This means that if we did go down the
expression-level-assignments-are-just-regular-name-binding-operations path,
then using the embedded assignment form in such cases could be discouraged
as misleadingly ambiguous by style guides and linters, without being
outright prohibited by the compiler itself.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180325/ea4f47c0/attachment.html>


More information about the Python-ideas mailing list