Python Custom Iterator:关闭StopIteration上的文件

时间:2013-02-10 12:53:33

标签: python file-io iterator

我编写了一个迭代器类,在其__init__中打开一个文件。

def __init__(self, path):
    self.file = open(path, "r")

迭代完成后如何自动关闭该文件?

完整课程:

class Parse(object):
    """A generator that iterates through a CC-CEDICT formatted file, returning
   a tuple of parsed results (Traditional, Simplified, Pinyin, English)"""
    def __init__(self, path):
        self.file = open(path, "r")

    def __iter__(self):
        return self

    def __is_comment(self, line):
        return line.startswith("#")

    def next(self):
        #This block ignores comments.
        line = self.file.readline()
        while line and self.__is_comment(line):
            line = self.file.readline()

        if line:
            working = line.rstrip().split(" ")
            trad, simp = working[0], working[1]
            working = " ".join(working[2:]).split("]")
            pinyin = working[0][1:]
            english = working[1][1:]
            return trad, simp, pinyin, english

        else:
            raise StopIteration()  

2 个答案:

答案 0 :(得分:11)

编写整个内容的更好方法是将开头和迭代保持在一个位置:

class Parse(object):
    """A generator that iterates through a CC-CEDICT formatted file, returning
    a tuple of parsed results (Traditional, Simplified, Pinyin, English)"""
    def __init__(self, path):
        self.path = path

    def __is_comment(self, line):
        return line.startswith("#")

    def __iter__(self):
        with open(self.path) as f:
            for line in f:
                if self.__is_comment(line):
                    continue

                working = line.rstrip().split(" ")
                trad, simp = working[0], working[1]
                working = " ".join(working[2:]).split("]")
                pinyin = working[0][1:]
                english = working[1][1:]
                yield trad, simp, pinyin, english

这将等待打开文件,直到您真正需要它,并在完成后自动关闭它。它的代码也少了。

如果你真的想进入“发电机真棒!”心态:

def skip_comments(f):
    for line in f:
        if not.startswith('#'):
            yield line

...

    def __iter__(self):
        with open(self.path) as f:
            for line in skip_comments(f):
                working = ....

答案 1 :(得分:1)

只要引发StopIteration,就需要明确关闭它。在这种情况下,只需在自己举起.close()时致电StopIteration

def next(self):
    #This block ignores comments.
    line = self.file.readline()
    while line and self.__is_comment(line):
        line = self.file.readline()

    if line:
        working = line.rstrip().split(" ")
        trad, simp = working[0], working[1]
        working = " ".join(working[2:]).split("]")
        pinyin = working[0][1:]
        english = working[1][1:]
        return trad, simp, pinyin, english

    else:
        self.file.close()
        raise StopIteration()  

由于.next()方法中没有其他代码可以触发StopIteration这就足够了。

如果 在您自己的next()内的另一个迭代器上使用.next(),则必须使用StopIteration处理程序捕获except StopIteration:再加上例外。

处理StopIteration案例。如果你想处理其他情况(不要耗尽迭代器),你需要分别处理这种情况。让您的班级成为Context Manager 以及也可以提供帮助。然后迭代器的用户在迭代它之前在with语句中使用该对象,并且当退出with套件时,无论如何都可以关闭该文件。在这种情况下,您可能希望将迭代器标记为“已完成”:

_closed = False

def next(self):
    if self._closed:
        raise StopIteration

    line = self.file.readline()
    while line and self.__is_comment(line):
        line = self.file.readline()

    if line:
        working = line.rstrip().split(" ")
        trad, simp = working[0], working[1]
        working = " ".join(working[2:]).split("]")
        pinyin = working[0][1:]
        english = working[1][1:]
        return trad, simp, pinyin, english

    else:
        self.file.close()
        self._closed = True
        raise StopIteration()  

def __enter__(self):
    return self

def __exit__(self, type_, value, tb):
    self.file.close()  # multiple calls to .close() are fine
    self._closed = True