2013-02-10 84 views
8

我已經編寫了一個迭代器類,用於打開文件__init__Python自定義迭代器:關閉StopIteration上的文件

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() 
+0

你可以分享迭代部分?你使用'.next()'或'.__ next __()'方法還是'__iter__'生成器方法? – 2013-02-10 12:55:00

+0

@MartijnPieters我將分享整個事情 – jsj 2013-02-10 12:59:05

回答

10

一種更好的方式來寫整個事情是保持在一個地方開迭代:

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 = .... 
+0

當生成器方法超出範圍時,它會自動清理,文件關閉,並且所有的快樂和花花公子! – 2013-02-10 13:16:05

+1

'__iter__'作爲發電機真的是最好的選擇。 – 2013-02-10 13:16:36

+0

唯一的缺點:如果你想訪問'.next()'方法,你需要調用'iter()'。小一點,可能不是OP的問題。 – 2013-02-10 13:17:01

1

你需要儘快StopIteration升高顯式關閉。在這種情況下,當您自己籌集StopIteration時,只需致電.close()即可。

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()你必須趕上StopIterationexcept StopIteration:處理程序,並再加註例外。

This only處理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 
+0

那麼當我從main中斷一個迭代時會發生什麼,是不是永遠不會到達? – jsj 2013-02-10 13:06:35

+0

@ trideceth12:在這一點上,你的迭代器永遠不會提升'StopIteration'。如果你想抓住這種情況,讓你的課程成爲上下文管理器*。 – 2013-02-10 13:07:35