Welcome, guest | Sign In | My Account | Store | Cart

Yielding from generator used inside another generator.

Python, 55 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from collections import deque

class continuator:
    def __init__(self, gen):
        self.stack = deque([gen])
    def __iter__(self):
        return self
    def next(self):
        try:
            gen = self.stack[-1]
        except IndexError:
            raise StopIteration
        try:
            ret_val = gen.next()
            if hasattr(ret_val, 'gi_frame'):
                self.stack.append(ret_val)
                return self.next()
            else:
                return ret_val
        except StopIteration:
            try:
                self.stack.pop()
                return self.next()
            except IndexError:
                raise StopIteration

#### EXAMPLE ###########################################################

def gen_abcd():
    for text in ['aaa','bbb','ccc','ddd']:
        yield text

def gen_efgh():
    for text in ['eee','fff','ggg','hhh']:
        yield text

def gen_ijkl():
    yield "iii"
    yield gen_jk()
    yield 'lll'

def gen_jk():
    yield "jjj"
    yield gen_k()

def gen_k():
    yield "kkk" 

def gen_all():
    yield gen_abcd()
    yield gen_efgh()
    yield gen_ijkl()

for text in continuator(gen_all()):
    print text

2 comments

Scott David Daniels 15 years, 10 months ago  # | flag

Simplifying continuator code. This code shows more clearly what the continuator is doing:

class continuator:
    def __init__(self, gen):
        self.stack = [iter(gen)]
    def __iter__(self):
    while self.stack:
        gen = self.stack.pop()
        for ret_val in gen:
        if hasattr(ret_val, 'gi_frame'):
            self.stack.extend([gen, iter(ret_val)])
        else:
            yield ret_val
Matt Goodall 15 years, 3 months ago  # | flag

Simpler still. Using a class seems overkill for this and gi_frame is most likely an implementation detail of generators. Also, why not make use of the wonderful itertools package:

from itertools import chain
from types import GeneratorType

def continuator(gen):
    while True:
        i = gen.next()
        if isinstance(i, GeneratorType):
            gen = chain(i, gen)
        else:
            yield i