【问题标题】:an instance of a generator class (recursive)生成器类的实例(递归)
【发布时间】:2012-11-22 11:39:39
【问题描述】:

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

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() 方法?

【问题讨论】:

  • 为什么要定义.close()
  • 这不是我期望的输出,为什么不是结果字符串?此外,您不应该使用type(each) is int,使用isinstance(each, int) 等...(很少需要检查确切的类型)
  • 这是一个简化的案例。最后在一个网络应用程序中,我将通过 WebOb.app_iter 从数据库中流式传输一些数据(递归生成),关闭会话(到数据库)的唯一方法是在我的生成器中的方法 .close() 中关闭它.
  • 你不能把它放在finally 子句中吗?它应该在所有值用完或生成器超出范围时运行。
  • 我认为您可以在基于函数的生成器中将close 调用作为GeneratorExit 异常捕获。在 Python 3.3 中,如果您使用新的 yield from 语法,即使对于递归生成器也可以直接使用(否则您需要有自己的代码来传播调用)。

标签: python recursion generator


【解决方案1】:

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

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

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")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-25
    • 2021-05-22
    • 2020-08-04
    • 2016-05-03
    • 2017-01-09
    • 2013-06-30
    • 2021-11-28
    • 2020-08-16
    相关资源
    最近更新 更多