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

Christoph Groth christoph at grothesque.org
Sat Mar 24 09:29:57 EDT 2018


Chris Angelico wrote:
> On Sat, Mar 24, 2018 at 8:07 PM, Christoph Groth
> <christ... at grothesque.org> wrote:
> > Chris Angelico wrote:
> >
> >> Thank you; both of these have now been incorporated into the document.
> >
> > Thanks!  Just a small comment.  You wrote in the PEP draft:
> >
> >> # Name bindings inside list comprehensions usually won't leak
> >> ...
> >> # But occasionally they will!
> >
> > I don't understand what you mean here.  If the (y as x) syntax is to
> > have, as you say, "the exact same semantics as regular assignment", then
> > assignments inside list comprehensions would always "leak".  But this is
> > a good thing, because this is consistent with how Python behaves.
> 
> 
> Except that a list comprehension is implemented using an inner
> function. Very approximately:
> 
> x = [n * m for n in range(4) for m in range(5)]
> 
> def <listcomp>(iter):
>     ret = []
>     for n in iter:
>         for m in range(5):
>             ret.append(n * m)
>     return ret
> x = <listcomp>(iter(range(4))

Indeed, the Python language reference section 6.2.4 says:

"Note that the comprehension is executed in a separate scope, so names
assigned to in the target list don’t “leak” into the enclosing scope."

As far as I can tell this has only consequences for the loop index
variables of the comprehension, because these are the only names that
are assigned values inside the comprehension.  After all, assignment is
currently a statement in Python, so it's not possible inside a list
comprehension to assign to other names.  (Or am I overlooking something
here?)

> So the first (outermost) iterable is actually evaluated in the
> caller's scope, but everything else is inside a subscope. Thus an
> assignment inside that first iterable WILL leak into the surrounding
> scope; but anywhere else, it won't.

It seems to me that this is a CPython implementation detail.

Couldn't

x = [n * m for n in range(4) for m in range(5)]

be equally well equivalent to

def <listcomp>():
    ret = []
    for n in range(4):
        for m in range(5):
            ret.append(n * m)
    return ret
x = <listcomp>()

As far as I can tell, with today's Python both implementations (yours
and mine) are indistinguishable by the user.

Since Python 3 decided to put comprehensions in a scope of their own,
that means that if an assignment operator ":=" is added to Python the
list comprehension

[(t := i**2, t**2) for i in (r := range(10))]

should neither leak r, nor t nor i.  Seems fine to me!

Christoph


More information about the Python-ideas mailing list