具有上下文管理器的生成器是反模式吗?

Hom*_*512 5 python garbage-collection generator reference-counting

我想知道这样的代码:

def all_lines(filename):
    with open(filename) as infile:
        yield from infile
Run Code Online (Sandbox Code Playgroud)

上下文管理器的目的是对某种形式的状态(例如文件句柄)的生命周期进行显式控制。另一方面,生成器会保持其状态,直到耗尽或删除。

我确实知道这两种情况在实践中都有效。但我担心这是否是一个好主意。例如考虑这个:

def all_first_lines(filenames):
    return [next(all_lines(filename), None) for filename in filenames]
Run Code Online (Sandbox Code Playgroud)

我从来没有耗尽发电机的电量。相反,当生成器对象被删除时,它们的状态就会被破坏。这在像 CPython 这样的引用计数实现中工作得很好,但是垃圾收集实现呢?我实际上依赖引用计数器来管理状态,这是上下文管理器明确设计要避免的!

即使在 CPython 中,如果生成器是引用循环的一部分并且需要销毁垃圾收集器,那么构建案例也不应该太难。

总结一下:您是否认为避免生成器中的上下文管理器是明智的,例如将上面的代码重构为这样的代码?

def all_lines(filename):
    with open(filename) as infile:
        return infile.readlines()

def first_line(filename):
    with open(filename) as infile:
        return next(infile, None)

def all_first_lines(filenames):
    return [first_line(filename) for filename in filenames]
Run Code Online (Sandbox Code Playgroud)

小智 1

虽然它确实延长了对象的生命周期,直到生成器退出或被销毁,但它也可以使生成器更易于使用。

考虑在外部 with 下创建生成器并将文件作为参数传递,而不是打开它。现在,在退出上下文管理器后,该文件将无法使用,尽管生成器仍然可以被视为可用。

如果限制句柄的持有时间很重要,则可以在使用完生成器后使用close 方法显式关闭生成器。

这与trio尝试使用其托儿所解决异步任务的问题类似,其中托儿所上下文管理器等待从该托儿所生成的每个任务退出后再继续,教程示例说明了这一点。作者的这篇博文可以为三人组的完成方式提供一些推理,这可能是一本有趣的读物,与问题有些相关。