生成器类的一个实例(递归)

时间:2012-11-22 11:39:39

标签: python recursion generator

我们说我们有这个递归函数(生成器)

data = [1,[21, 22, [231, 232, 233]], [31, 32, 33]]

def listgen(data):
    for each in data:
        if type(each) is int:
            yield str(each)
        elif type(each) is list:
            g = listgen(each)
            for i in g:
                yield i
        else:
            continue
gen = listgen(data)
print ' '.join(gen)

输出是:

1 21 22 231 232 233 31 32 33

现在......我怎么能把它写成一个类,因为我需要为那个生成器定义.close()方法?

1 个答案:

答案 0 :(得分:2)

当您在生成器上调用close时,它会在其执行当前暂停的位置引发GeneratorExit。您可以捕获该异常并在重新引发异常之前在except块中进行清理(不允许忽略它)。

对于递归生成器来说,这有点复杂,因为您需要手动将递归堆栈中的异常传播。但即便如此也不难。这是您的生成器的一个版本,在关闭时,在每个执行级别上打印“清理”。如果在它从示例数据集中产生231之后关闭它,您将获得三次,一个用于列表中的每个嵌套层。在您的实际用例中,您可以将其替换为代码以关闭数据库连接,或者执行您必须执行的任何其他清理工作。

def closable_listgen(data):
    try:
        for each in data:
            if type(each) is int:
                yield str(each)
            elif type(each) is list:
                g = closable_listgen(each)
                try:
                    for i in g:
                        yield i
                except GeneratorExit:
                    g.close()
                    raise
            else:
                continue
    except GeneratorExit:
        print("cleaning up")
        raise

Python 3.3使这一点变得更简单,因为新的yield from语法会自动将调用传递到close向上的生成器链:

def closable_listgen33(data):
    try:
        for each in data:
            if type(each) is int:
                yield str(each)
            elif type(each) is list:
                g = closable_listgen33(each)
                yield from g
            else:
                continue
    except GeneratorExit:
        print("cleaning up")
        raise

为了更好的衡量,以下是如何将生成器实现为类(这是您最初要求的):

class list_gen_class(object):
    def __init__(self, data):
        self.iterator = iter(data)
        self.child = None

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            if self.child:
                try:
                    return next(self.child)
                except StopIteration:
                    self.child = None

            value = next(self.iterator)
            if isinstance(value, int):
                return str(value)
            elif isinstance(value, list):
                self.child = list_gen_class(value)

    def close(self):
        if self.child:
            self.child.close()

        print("cleaning up")