Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用打开的文件句柄从Python生成器中断_Python_Generator_Break - Fatal编程技术网

如何使用打开的文件句柄从Python生成器中断

如何使用打开的文件句柄从Python生成器中断,python,generator,break,Python,Generator,Break,我正在写一个Python生成器,它看起来像“猫”。我的具体用例是“类似grep”的操作。我希望它能够在满足以下条件时从发电机中断开: summary={} for fn in cat("filelist.dat"): for line in cat(fn): if line.startswith("FOO"): summary[fn] = line break 因此,当发生break时,我需要cat()生成器完成并关闭fn

我正在写一个Python生成器,它看起来像“猫”。我的具体用例是“类似grep”的操作。我希望它能够在满足以下条件时从发电机中断开:

summary={}
for fn in cat("filelist.dat"):
    for line in cat(fn):
        if line.startswith("FOO"):
            summary[fn] = line
            break
因此,当发生
break
时,我需要
cat()
生成器完成并关闭
fn
的文件句柄

我必须读取100k个文件,总数据为30GB,并且
FOO
关键字出现在标题区域,因此在这种情况下,
cat()
函数尽快停止读取文件非常重要

我还有其他方法可以解决这个问题,但我仍然有兴趣知道如何从具有打开的文件句柄的生成器中尽早退出。也许Python会立即清理它们,并在生成器被垃圾收集时关闭它们

谢谢


Ian

生成器有一个
close
方法,在
yield
语句中引发
GeneratorExit
。如果您专门捕获此异常,则可以运行一些分解代码:

import contextlib
with contextlib.closing( cat( fn ) ):
    ...
然后在
cat
中:

try:
    ...
except GeneratorExit:
    # close the file

如果您想要一种更简单的方法(不在生成器上使用神秘的
close
方法),只需让
cat
打开一个类似文件的对象而不是字符串,然后自己处理文件IO:

for filename in filenames:
    with open( filename ) as theFile:
        for line in cat( theFile ):
            ...

但是,您基本上不需要担心这些,因为垃圾收集将处理所有这些。尽管如此

显性比隐性好

通过在同一对象中实现和,您可以编写如下非常好的代码:

with cat("/etc/passwd") as lines:
    for line in lines:
        if "mail" in line:
            print line.strip()
            break
这是一个示例实现,在Linux机器上使用Python 2.5进行了测试。它读取
/etc/passwd
的行,直到找到用户
音频的行,然后停止:

from __future__ import with_statement


class cat(object):

    def __init__(self, fname):
        self.fname = fname

    def __enter__(self):
        print "[Opening file %s]" % (self.fname,)
        self.file_obj = open(self.fname, "rt")
        return self

    def __exit__(self, *exc_info):
        print "[Closing file %s]" % (self.fname,)
        self.file_obj.close()

    def __iter__(self):
        return self

    def next(self):
        line = self.file_obj.next().strip()
        print "[Read: %s]" % (line,)
        return line


def main():
    with cat("/etc/passwd") as lines:
        for line in lines:
            if "mail" in line:
                print line.strip()
                break


if __name__ == "__main__":
    import sys
    sys.exit(main())
或者更简单:

with open("/etc/passwd", "rt") as f:
    for line in f:
        if "mail" in line:
            break

文件对象实现迭代器协议(请参见)

使用try..finally(在Python 2.7.6上测试)似乎还有另一种可能性:

给我以下打印输出:

yield 0
0
yield 1
1
yield 2
break
done

也请考虑这个例子:

def itertest():
    try:
        for i in xrange(1000):
            print i
            yield i
    finally:
        print 'finally'

x = itertest()

for i in x:
    if i > 2:
        break

print 'del x'
del x

print 'exit'

0
1
2
3
del x
finally
exit

它显示在清理迭代器之后运行finally。我想
\uu del\uuu(self)
正在调用
self.close()
,请参见此处:

如果您问我,我会将所有清理代码放入finally子句中。我也用izip()和两个生成器测试了这一点,其中另一个生成的元素更少。然而,我刚刚编写的代码很复杂,没有触及finally子句——我正在调查为什么会这样。
def itertest():
    try:
        for i in xrange(1000):
            print i
            yield i
    finally:
        print 'finally'

x = itertest()

for i in x:
    if i > 2:
        break

print 'del x'
del x

print 'exit'

0
1
2
3
del x
finally
exit