如何使用打开的文件句柄从Python生成器中断
我正在写一个Python生成器,它看起来像“猫”。我的具体用例是“类似grep”的操作。我希望它能够在满足以下条件时从发电机中断开:如何使用打开的文件句柄从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
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