生成器可以管理资源,例如通过yield
从上下文管理器内部 'ing。一旦close()
调用生成器的方法(或引发异常),资源就会被释放。
由于最后很容易忘记调用close()
,我认为为此使用上下文管理器(以及处理潜在的异常)是显而易见的。我知道我可以使用它,但是在语句contextlib.closing
中直接使用生成器不是更好吗?with
为什么生成器不应该是上下文管理器?
生成器可以管理资源,例如通过yield
从上下文管理器内部 'ing。一旦close()
调用生成器的方法(或引发异常),资源就会被释放。
由于最后很容易忘记调用close()
,我认为为此使用上下文管理器(以及处理潜在的异常)是显而易见的。我知道我可以使用它,但是在语句contextlib.closing
中直接使用生成器不是更好吗?with
为什么生成器不应该是上下文管理器?
一般来说,您没有看到更多生成器作为上下文管理器的原因是它们旨在解决不同的问题。上下文管理器的出现是因为它提供了一种将可执行代码范围限定为资源的简洁明了的方式。
__iter__()
您可能希望将实现的类与同时作为上下文管理器的类分开,这是一个很好的理由,即单一职责原则。单一职责归结为概念
让班级做一件事并做好
列表是可迭代的,但那是因为它们是一个集合。除了他们持有的东西之外,他们不管理任何状态,而迭代只是访问该状态的另一种方式。除非您需要将迭代作为访问包含对象状态的一种方式,否则我看不出将两者混合和匹配的理由。即便如此,我还是会竭尽全力以真正的 OO 风格将其分离出来。
就像 Wheaties 说的那样,你想让班级只做“一件事并做好”。特别是对于上下文管理器,他们正在管理一个上下文。所以问问自己,这里的上下文是什么?大多数情况下,它将打开一个资源。前段时间,我询问了有关使用带有上下文管理器的队列的问题,得到的回答基本上是队列作为上下文没有意义。然而,“在一项任务中”是我所处的真实环境,为此创建一个上下文管理器是有意义的。
此外,没有迭代with
语句。例如,我无法打开一个文件并在这样的一个语句中遍历它:
for line in file with open(filename) as file:
...
它必须分两行完成:
with open(filename) as file:
for line in file:
...
这很好,因为被管理的上下文不是“我们正在遍历文件”,而是“我们打开了一个文件”。再说一遍,上下文是什么?你到底在做什么?最有可能的是,您的托管上下文实际上并不是对资源的迭代。但是,如果您查看您的具体问题,您可能会发现您确实遇到了生成器正在管理上下文的情况。希望了解上下文的真正含义会给您一些关于如何适当管理它的想法。