代码之家  ›  专栏  ›  技术社区  ›  IanSR

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

  •  4
  • IanSR  · 技术社区  · 16 年前

    我在写一个类似于“cat”的python生成器。我的特定用例用于“grep-like”操作。我希望它能够在满足以下条件时脱离发电机:

    summary={}
    for fn in cat("filelist.dat"):
        for line in cat(fn):
            if line.startswith("FOO"):
                summary[fn] = line
                break
    

    所以什么时候 break 碰巧,我需要 cat() 要完成并关闭文件句柄的生成器 fn .

    我必须读取总数据为30GB的100K文件,并且 FOO 关键字发生在标题区域中,因此在这种情况下, () 函数停止尽快读取文件。

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

    谢谢,

    伊恩

    4 回复  |  直到 8 年前
        1
  •  5
  •   Carlos Valiente    16 年前

    通过实施 context protocol 以及 iterator protocol 在同一个对象中,您可以这样编写非常好的代码:

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

    这是一个示例实现,在Linux设备上用python 2.5进行测试。它读的是 /etc/passwd 直到找到适合用户的 audio ,然后停止:

    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
    

    文件对象实现迭代器协议(请参见 http://docs.python.org/library/stdtypes.html#file-objects )

        2
  •  5
  •   Adam    8 年前

    发电机有一个 close 提出的方法 GeneratorExit yield 语句。如果您特别捕获了这个异常,您可以运行一些下拉代码:

    import contextlib
    with contextlib.closing( cat( fn ) ):
        ...
    

    然后在 cat :

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

    如果你想要一个更简单的方法来做这个(不使用奥术 关闭 发电机的方法),只需制造 打开一个类似文件的对象而不是字符串,然后自己处理文件IO:

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

    但是,您基本上不需要担心这些问题,因为垃圾收集将处理所有这些问题。仍然,

    显式优于隐式

        3
  •  1
  •   Matthias    11 年前

    请考虑这个例子:

    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
    

    它显示了在清理迭代器之后最终运行。我想 __del__(self) 正在呼叫 self.close() ,另请参见此处: https://docs.python.org/2.7/reference/expressions.html#generator.close

        4
  •  0
  •   Matthias    11 年前

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

    def gen():
        i = 0
        try:
            while True:
                print 'yield %i' % i
                yield i
                i += 1
            print 'will never get here'
        finally:
            print 'done'
    
    for i in gen():
        if i > 1:
            print 'break'
            break
        print i
    

    提供以下打印输出:

    yield 0
    0
    yield 1
    1
    yield 2
    break
    done