Generator (re-)definition within a loop
Terry Reedy
tjreedy at udel.edu
Mon Jun 21 14:17:52 EDT 2010
On 6/21/2010 3:29 AM, Pierre Reinbold wrote:
> On 06/18/2010 11:48 PM, Terry Reedy wrote:
>> Let's apply Reedy's Rule: when you have trouble understanding a function
>> expression, replace it with the (near) equivalent def statement. (Among
>> other advantages, one can insert print calls!)
>>
>> Genexps, like lambdas, are specialized function expressions.
>>
>> def augment(s_of_s, s):
>> for x in s_of_s:
>> for y in s:
>> yield x+[y]
>>
>> def gen_product(*args):
>> pools = map(tuple, args)
>> result = [[]]
>> for pool in pools:
>> result = augment(result,pool)
>> for prod in result:
>> yield tuple(prod)
>>
>> print(list(gen_product("ABC", "xy")))
>>
>>>>> #3.1
>> [('A', 'x'), ('A', 'y'), ('B', 'x'), ('B', 'y'), ('C', 'x'), ('C', 'y')]
>
> Very instructive post ! Thank you !
> Just trying to understand, I have apply the Reedy's Rule in an attempt
> to reproduce the same behaviour as the generator expression.
I intentionally said '(near) equivalent' because, as you seem to have
found, exactly reproducing a bug in a function expression with a def
statement may be difficult to impossible. The form I applied above was
'equivalent to what one *thought* one was writing'. If one just wants to
fix the bug without completely understanding it, that should be enough.
> The idea, I
> guess, is to (re-)define the generator function inside the loop.
To produce a similar bug, in this case, yes.
>
> My first try gives this:
>
> def badgen_product1(*args, **kwds):
> pools = map(tuple, args)
> result = [[]]
> for pool in pools:
> def result():
> for x in result():
> for y in pool:
> yield x+[y]
> for prod in result():
> yield tuple(prod)
>
> But this does not reproduce the generator expression, it leads naturally
> to an infinite recursion (which is what I expected first for the
> generator expression btw)
>
> for x in result():
> RuntimeError: maximum recursion depth exceeded
>
> Another try to avoid infinite recursion:
>
> def badgen_product2(*args, **kwds):
> pools = map(tuple, args)
> result = [[]]
> for pool in pools:
> def augments():
> for x in result:
> for y in pool:
> yield x+[y]
> result = augments()
> for prod in result:
> yield tuple(prod)
>
> And this one gives the infamous:
>
> for x in result:
> ValueError: generator already executing
>
> Which seems to indicate that the lazy evaluation leads to eventually
> bind everything to the same generator.
>
> So, neither of my attempts reproduce the behaviour of the generator
> expression. What do I miss here ?
See above. Exact reproduction is not always possible.
>
> Thank for your help,
You are welcome. I hope you learned something. Wrestling with code is a
good way to do that. I love Python because it makes it so easy to do that.
--
Terry Jan Reedy
More information about the Python-list
mailing list