On 5/7/2018 1:38 PM, Guido van Rossum wrote:
I am convinced by Tim's motivation. I hadn't thought of this use case
before -- I had mostly thought "local scope in a comprehension or
generator expression is the locals of the synthetic function". But Tim's
reasoning feels right.
The only solution that makes sense to me is Steven's (2). (1) is what
the PEP currently says and what Tim doesn't want; (3) has no precedent
(function defaults don't really work like this) and just gets my hackles
all up. (I can't even tell if Steven meant it as a serious proposal.)
So let's see if we can change PEP 572 so that := inside a comprehension
or generator expression al ways assigns to a variable in the containing
scope.
It may be inconsistent with the scope of the loop control variable, but
it's consistent with uses of := outside comprehensions:
[x := 0]
[x := i for i in range(1)]
both have the side effect of setting x to zero. I like that.
If I am understanding correctly, this would also let one *intentionally
'leak' (export) the last value of the loop variable when wanted.
[math.log(xlast:=x) for x in it if x > 0]
print(xlast)
There's one corner case (again) -- class scopes. If the containing scope
is a function, everything's fine, we can use the existing closure
machinery. If the containing scope is global, everything's fine too, we
can treat it as a global. But if the containing scope is a class, we
can't easily extend the current machinery. But this breakage is similar
to the existing breakage with comprehensions in class scope that
reference class variables:
class C:
hosts = ['boring', 'haring', 'tering']
full_hosts = [host + suffix for suffix in ('.cwi.nl
<http://cwi.nl>', '.com') for host in hosts]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in C
File "<stdin>", line 3, in <listcomp>
NameError: name 'hosts' is not defined
This is a special case of the fact that no function called in class
scope can access class variables, even if defined in the class scope.
>>> class C:
x = 0
def f():
return x
z = f()
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
class C:
File "<pyshell#5>", line 5, in C
z = f()
File "<pyshell#5>", line 4, in f
return x
NameError: name 'x' is not defined
I would find it strange if only functions defined by a comprehension
were given new class scope access.
PS1. The various proposals that add random extra keywords to the syntax
(like 'for nonlocal i') don't appeal to me at all.
PS2. IIRC the reason we gave loop control variables their own scope was
the poor imagination of many people when it comes to choosing loop
control variable names. We had seen just too many examples of
for x in something:
...lots of code using x...
blah blah [x+1 for x in something else]
...some more code using x, broken...
It's true that this can also happen with a for-loop statement nested
inside the outer loop (and it does) but the case of a comprehension was
easier to miss. I've never looked back.
PS3. Comprehensions and generator expressions should be interchangeable.
They just look too similar to have different semantics (and the possibly
delayed execution of generator expressions is not an issue -- it's rare,
and closure semantics make it work just fine).
To me, this is the prime justification for the 3.0 comprehension change.
I currently see a comprehension as a specialized generator expression.
A generator expression generalizes math set builder notation. If left
'raw', the implied function yields the values generated (what else could
it do?). If a collection type is indicated by the fences and expression
form, values are instead added to an anonymous instance thereof.
--
Terry Jan Reedy
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/