2013-02-10 11 views
8

__init__ numaralı dosyada bir dosya açan bir yineleme sınıfı yazdım.Python Özel Yineleyici: StopIteration'da bir dosyayı kapatın

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

nasıl yineleme bittikten otomatik olarak bu dosyayı kapatmak mı?

Komple sınıfı:

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

Yineleme bölümünü paylaşabilir misiniz? '.next()' veya '.__ sonraki __()' metodunu mu kullanıyorsunuz yoksa '__iter__' bir jeneratör metodu mu? –

+0

@MartijnPieters Ben her şeyi paylaşacağım – jsj

cevap

10

tek bir yerde açılma ve yinelemeyi tutmak olacaktır şeyi yazmak için daha iyi bir yolu:

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 

Dosya gerçekten ihtiyacınız olana kadar dosyayı açmak için bekleyecek ve bittiğinde otomatik olarak kapanacaktır. Aynı zamanda daha az kod.

Gerçekten "jeneratörler harika!" zihniyet:

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

Jeneratör yöntemi kapsam dışında kaldığında, otomatik olarak temizlenir, dosya kapatılır ve hepsi de mutludur! Jeneratör olarak –

+1

'__iter__' gerçekten gitmek için en iyi yoldur. –

+0

Sadece dezavantaj: '.next()' yöntemine erişmek istiyorsanız, 'iter()' yi çağırmalısınız. Küçük olan, muhtemelen OP için bir sorun değil. –

1

Açıkça kısa sürede StopIteration yükseltilir olarak kapatmak gerekir. Bu durumda, StopIteration'u kendiniz yükselttiğinizde .close()'u arayın.

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

sizin .next() yönteminde başka hiçbir kod, bu yeterli bir StopIteration tetikleyebilir beri. Eğer .next() kendi içinde başka yineleyici üzerinde kullanımını next() yaptıysak

bir except StopIteration: işleyicisi ile StopIteration yakalamak ve istisna tekrar yükseltme gerekirdi.

Bu yalnızca, StopIteration örneğini işler. Diğer durumlarla (yineleyiciyi yormamakla) ilgilenmek isterseniz, bu durumu ayrı ayrı ele almanız gerekir. Sınıfınızda Context Manager'un yanı sıra da buna yardımcı olabilir. Yineleyicinizin kullanıcıları, daha sonra yinelemeden önce nesneyi with ifadesinde kullanır ve with paketinden çıkıldığında dosya ne olursa olsun kapatılabilir. bu durumda hem 'done' gibi Eğer yineleyici işaretlemek isteyebilirsiniz:

_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

Peki, ana bir yinelemeyi kırdığımda, asla ulaşılamayacak mıyım? – jsj

+0

@ trideceth12: bu noktada yineleyiciniz “StopIteration” ı hiçbir zaman yükseltmez. Bu durumu yakalamak istiyorsanız, sınıfınızı bir İçerik Yöneticisi * de yapın *. –

İlgili konular