Python生成器如何在调用生成器的for循环突然返回时关闭文件句柄?

时间:2014-08-09 06:31:56

标签: python

如果函数lookForSpecificLine返回True(也就是说,如果文件“foo.txt”包含targetLine),Python如何知道关闭文件句柄?文件“foo.txt”会保持打开状态吗?

def lines(filename):
    with open(filename, encoding='utf-8') as file:
        for line in file:
            yield line

def lookForSpecificLine(targetLine):
    for line in lines('foo.txt'):
        if targetLine == line:
            return True
    return False

3 个答案:

答案 0 :(得分:11)

只要生成器对象处于活动状态,您的文件就会保持打开状态。当生成器被垃圾收集时(通常在lookForSpecificLine函数的末尾),Python会在其上调用close,作为PEP 342中描述的协同例程协议的一部分。 close方法导致Python在生成器代码暂停的位置(在GeneratorExit语句之后)抛出yield异常。由于您没有捕获到该异常(因为您通常不应该这样做),因此它将跳出循环并导致with语句关闭该文件。

请注意,如果lookForSpecificLine更复杂,并且存在导致异常的风险(可能会在更高级别捕获),则可能无法快速清理。这是因为异常回溯会使函数的堆栈帧保持活动状态,因此不会立即对生成器进行垃圾回收,也不会关闭文件。

答案 1 :(得分:1)

这是有效的,因为文件对象也是上下文管理器。基本上,类需要定义__enter____exit__函数,它们将分别在with块的开头和结尾处调用。

这是一个简单的上下文管理器示例,它在with块的开头和最后的“退出”上打印“on enter”:

class contextmanager:
    def __enter__(self):
        print('on enter')
    def __exit__(self, type, value, traceback):
        print('on exit')

以及使用它的示例:

>>> with contextmanager():
...     print('inside with')
...
on enter
inside with
on exit

现在让我们尝试在with语句中引发异常:

>>> with contextmanager():
...     raise Exception()
...
on enter
on exit
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception

您可以看到__exit__函数内的代码在with语句的末尾被调用,无论它是正常执行还是引发了异常。对于文件对象,它们使用此函数来关闭文件句柄并清理。

答案 2 :(得分:0)

离开with块后文件将关闭。参见例如有关with声明的更多信息,请PEP343this guide