2013-02-10 12 views
8

Napisałem klasę iteratora, która otwiera plik w pliku __init__.Python Custom Iterator: Zamknij plik na StopIteration

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

Jak zamknąć ten plik automatycznie po zakończeniu iteracji?

pełna klasa:

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

Czy możesz udostępnić część iteracji? Czy używasz metody '.next()' lub '.__ next __()' lub czy '__iter__' jest metodą generatora? –

+0

@MartijnPieters Podzielę się całą rzeczą – jsj

Odpowiedz

10

Lepszym sposobem napisać całość byłoby utrzymanie otwieranie i iteracji w jednym miejscu:

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 

Spowoduje to oczekiwanie na otwarcie pliku, dopóki go naprawdę nie potrzebujesz i po zakończeniu zostanie automatycznie zamknięty. To także mniej kodu.

Jeśli naprawdę chcesz dostać się do "generatorów są niesamowite!" nastawienie:

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

A gdy metoda generatora wykracza poza zakres, jest automatycznie czyszczona, plik jest zamknięty, a wszystko jest szczęśliwe i eleganckie! –

+1

'__iter__' jako generator jest naprawdę najlepszym sposobem. –

+0

Jedyny mankament: Musisz wywołać 'iter()', jeśli chcesz uzyskać dostęp do metody '.next()'. Mały, prawdopodobnie nie jest problemem dla PO. –

1

Trzeba wyraźnie go zamknąć jak najszybciej StopIteration jest podniesiony. W takim przypadku wystarczy zadzwonić pod numer .close(), gdy samodzielnie podnosisz numer 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() 

Ponieważ żaden inny kod w metodzie .next() może wywołać StopIteration to wystarczy.

Jeśli zrobił korzystanie next() na innym iterator wewnątrz własnych .next() trzeba by złapać StopIteration z except StopIteration: obsługi i przebić wyjątek.

Ten tylko obsługuje obudowę StopIteration. Jeśli chcesz zająć się innymi sytuacjami (nie wyczerpując iteratora), musisz zająć się tą sytuacją oddzielnie. Sprawienie, że twoja klasa będzie także Context Manageri może w tym pomóc. Użytkownicy iteratora będą następnie używać obiektu w oświadczeniu with przed iterowaniem nad nim, a po wyjściu z zestawu with plik może zostać zamknięty niezależnie. Możesz oznaczyć iterator jako „zrobione”, jak również w tym przypadku:

_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

Co się stanie, gdy przerwiesz iterację od głównej, czy nigdy więcej nie zostanie osiągnięty? – jsj

+0

@ trideceth12: w tym momencie twój iterator nigdy nie podniesie "StopIteration". Jeśli chcesz złapać tę sytuację, ustaw swoją klasę jako Context Manager * również *. –