Reputation: 6781
My code uses yield from
in python3
in recursive calls and it works perfectly fine. The problem right now is that this was introduced from PEP-380 in python 3.3
and I need it to work in python 2.7
. I read up on a few articles and none of them were detailed enough or simple enough.
Few referred articles :
and few others.
I have recreated a small Sample code (which takes in a multi-level list and returns a flattened list) that is very minimalistic compared to my requirements.
#python 3
def foo(obj):
for ele in obj:
if isinstance(ele, list):
yield from foo(ele)
else:
yield ele
#driver values :
>>> l = [1, [2, 3, [4,5]]]
>>> list(foo(l))
=> [1, 2, 3, 4, 5]
The same converted does not work in python 2.7
due to the non-availability of yield from
.
Upvotes: 1
Views: 1105
Reputation: 1122172
You still need to loop. It doesn't matter that you have recursion here.
You need to loop over the generator produced by the recursive call and yield the results:
def foo(obj):
for ele in obj:
if isinstance(ele, list):
for res in foo(ele):
yield res
else:
yield ele
Your recursive call produces a generator, and you need to pass the results of the generator onwards. You do so by looping over the generator and yielding the individual values.
There are no better options, other than upgrading to Python 3.
yield from
essentially passes on the responsibility to loop over to the caller, and passes back any generator.send()
and generator.throw()
calls to the delegated generator. You don't have any need to pass on .send()
or .throw()
, so what remains is taking responsibility to do the looping yourself.
Demo:
>>> import sys
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=14, releaselevel='final', serial=0)
>>> def foo(obj):
... for ele in obj:
... if isinstance(ele, list):
... for res in foo(ele):
... yield res
... else:
... yield ele
...
>>> l = [1, [2, 3, [4,5]]]
>>> list(foo(l))
[1, 2, 3, 4, 5]
yield from
was introduced in PEP 380 -- Syntax for Delegating to a Subgenerator (not PEP 342), specifically because a loop over the sub-generator would not delegate generator.throw()
and generator.send()
information.
The PEP explicitly states:
If yielding of values is the only concern, this can be performed without much difficulty using a loop such as
for v in g: yield v
The Formal Semantics has a Python implementation equivalent that may look intimidating at first, but you can still pick out that it loops (with while 1:
, looping ends when there is an exception or StopIteration
is handled, new values are retrieved with next()
or generator.send(..)
), and yields the results (with yield _y
).
Upvotes: 3
Reputation: 55479
Why do you say "my code cannot work with loops and needs to be recursive"? You can easily use a loop in a recursive generator:
def foo(obj):
for ele in obj:
if isinstance(ele, list):
#yield from foo(ele)
for t in foo(ele):
yield t
else:
yield ele
l = [1, [2, 3, [4, 5]]]
print list(foo(l))
output
[1, 2, 3, 4, 5]
Upvotes: 1