A gotcha: Python pain point?
Terry Reedy
tjreedy at udel.edu
Tue Jun 12 04:46:19 EDT 2007
"James Stroud" <jstroud at mbi.ucla.edu> wrote in message
news:f4knb2$mn6$1 at zinnia.noc.ucla.edu...
| Beorn wrote:
| > Consider this example:
| >
| > >>> def funcs(x):
| > ... for i in range(5):
| > ... def g(): return x + i
| > ... yield g
| >
| > >>> [ fun() for fun in list(funcs(1)) ]
| > [5, 5, 5, 5, 5]
| >
| > Whereas:
| >
| > >>> [ fun() for fun in funcs(1) ]
| > [1, 2, 3, 4, 5]
| If this isn't classified as a bug,
It is not, it is well-documented behavior. Still, suggestions for
improvement might be considered.
| Why would it be desirable for a generator to behave
| differently in two different contexts.
I have no idea.
Each call of the generator function funcs behaves the same. It returns a
generator that yields 5 identical copies of the inner function g. The
multiple copies are not needed and only serve to confuse the issue.
Changing funcs to return a generator that yields the *same* function (five
times) gives the same behavior.
def funcs(x):
def g(): return x + i
for i in range(5):
yield g
print [ fun() for fun in list(funcs(1)) ]
print [ fun() for fun in funcs(1) ]
>>> # when run
[5, 5, 5, 5, 5]
[1, 2, 3, 4, 5]
What matters is the value of g's nonlocal var i (funcs' local var i) when
the yielded function g is *called*.
The difference between returning versus yielding an inner closure such as g
is this. If g is returned, the outer function has terminated and the
enclosed variable(s), i in this case, is frozen at its final value. If g
is yielded, the enclosed i is *live* as long as the generator is, and its
values can change between calls, as in the second print statement.
| Should I import this to see how
| many principles this behavior violates?
???
If you meant 'report' (on SF), please do not.
Terry Jan Reedy
More information about the Python-list
mailing list