
Urgh, we need this sorted out before Raymond can rewrite PEP 289 and present it to c.l.py...
[Samuele Pedroni]
so this, if I understand:
def h(): y = 0 l = [1,2] it = (x+y for x in l) y = 1 for v in it: print v
will print 1,2 and not 2,3
That is what I had in mind, and that if the first assignment to "y" were commented out, the assignment to "it" would raise UnboundLocalError.
unlike:
def h(): y = 0 l = [1,2] def gen(S): for x in S: yield x+y it = gen(l) y = 1 for v in it: print v
Yes, but like it if you replaced the "def gen" and the line following it with:
def gen(y=y, l=l): for x in l: yield x+y it = gen()
This is worth some thought. My intuition is that we *don't* want "a closure" here. If generator expressions were reiterable, then (probably obnoxiously) clever code could make some of use of tricking them into using different inherited bindings on different (re)iterations. But they're one-shot things, and divorcing the values actually used from the values in force at the definition site sounds like nothing but trouble to me (error-prone and surprising). They look like expressions, after all, and after
x = 5 y = x**2 x = 10 print y
it would be very surprising to see 100 get printed. In the rare cases that's desirable, creating an explicit closure is clear(er):
x = 5 y = lambda: x**2 x = 10 print y()
I expect creating a closure instead would bite hard especially when building a list of generator expressions (one of the cases where delaying generation of the results is easily plausible) in a loop. The loop index variable will probably play some role (directly or indirectly) in the intended operation of each generator expression constructed, and then you severely want *not* for each generator expression to see "the last" value of the index vrlbl.
Right.
For concreteness, test_generators.Queens.__init__ creates a list of rowgen() generators, and rowgen uses the default-arg trick to give each generator a different value for rowuses; it would be an algorithmic disaster if they all used the same value.
Generator expressions are too limited to do what rowgen() does (it needs to create and undo side effects as backtracking proceeds), so it's not perfectly relevant as-is. I *suspect* that if people work at writing concrete use cases, though, a similar thing will hold.
BTW, Icon can give no guidance here: in that language, the generation of a generator's result sequence is inextricably bound to the lexical occurrence of the generator. The question arises in Python because definition site and generation can be divorced.
So, do you want *all* free variables to be passed using the default-argument trick (even globals and builtins), or only those that correspond to variables in the immediately outer scope, or only those corresponding to function scopes (as opposed to globals)? n = 0 def f(): global n n += 1 return n print list(n+f() for x in range(10)) --Guido van Rossum (home page: http://www.python.org/~guido/)