这是一个可以接受的pythonic成语吗?

问题描述:

我有一个辅助导入特殊类型的文件的类和一个允许我批量执行这些操作的“工厂”类。工厂类使用生成器,客户端可以遍历导入器。 我的问题是,我是否正确使用迭代器?这是一个可以接受的习惯用法吗?我刚开始使用Python。这是一个可以接受的pythonic成语吗?

class FileParser: 
    """ uses an open filehandle to do stuff """ 

class BatchImporter: 
    def __init__(self, files): 
    self.files=files 

    def parsers(self): 
    for file in self.files: 
     try: 
     fh = open(file, "rb") 
     parser = FileParser(fh) 
     yield parser 
     finally: 
     fh.close() 

    def verifyfiles(
    def cleanup(

--- 

importer = BatchImporter(filelist) 
for p in BatchImporter.parsers(): 
    p.method1() 
    ... 
+2

将'each'换成'for',它对我来说很不错 – jcao219 2010-07-19 17:41:25

+0

唉! thx perl ... – mikewaters 2010-07-19 22:14:38

像你一样拥有一个生成器的方法是绝对没问题的。我会建议让你的所有类都变成新的样式(如果你使用的是Python 2,你可以在模块的开始处设置__metaclass__ = type,或者将(object)添加到你的所有基类的class语句中),因为遗留类是“邪恶”的。 - );并且,为了清晰和简洁,我也会不同电子书籍编码发生器...!

def parsers(self): 
    for afile in self.files: 
     with open(afile, "rb") as fh: 
      yield FileParser(fh) 

,但以任何方式都不建议这些位的谴责使用发电机方法 - )

注使用代替file:后者是一个内置的标识符,作为一般规则,最好是习惯于而不是用你自己的“隐藏”内置标识符(它不会在这里咬你) ,但是,除非你有正确的习惯,否则它将在未来以许多讨厌的方式出现 - )。

+0

我同意100%遗传类是“邪恶”。 – jcao219 2010-07-19 17:50:52

+0

@jcao,是的,幸运的是,他们已经在Python 3中消失了(有一天,这就是我们将会使用的所有东西......只是当你等待那一天时不要屏住呼吸!)。 – 2010-07-19 18:03:20

+0

谢谢Alex。我最大的问题是在常规方法中使用生成器(而不是__iter__),感谢您的答案。我发现我必须在“importer.parsers()”中使用“parser”,特别是括号,令人不安;我的常识告诉我,'解析器'会是某种类的集合,而不是一种方法,并且这些parens在这方面令人困惑。但是,我不知道任何其他方式来呈现需要每个实例初始化和清理(文件打开/关闭)的对象集合。 谢谢! – mikewaters 2010-07-21 16:38:35

你可以做一件事稍微简单:与其try ...... finally,使用with块:

with open(file, "rb") as fh: 
    yield FileParser(fh) 

这将立即自动关闭该文件送给你作为with块离开了。

+0

但请注意,'with'块在下次访问发生器之前并不真正“离开”。 (在此之前,执行被暂停在'yield'声明中。) – 2010-07-19 18:15:08

+0

'with'也相当新...不是py 2.6吗?这必须是多么便携? – Chris 2010-07-19 18:47:54

如果你问我,设计是好的,尽管最终你使用它的方式不完全是惯用的。使用catch,也许重新提高异常(使用raise关键字单独,否则你搞砸堆栈跟踪),并为奖励点,不要抓住:但赶上例外:(否则,你赶上SystemExit和KeyboardInterrupt)。

或者简单地使用Tim Pietzcker所示的with-statement。

通常,在产生将尝试读取文件的解析器对象后关闭文件是不安全的。考虑以下代码:

parsers = list(BatchImporter.parsers()) 
for p in parsers: 
    # the file object that p holds will already be closed! 

如果你不写一个长期运行的守护进程,大部分的时间你不需要担心关闭文件 - 他们都将得到关闭,当你的程序退出或当文件对象被垃圾收集时。 (如果你使用CPython,只要所有对它们的引用丢失,就会发生这种情况,因为CPython使用引用计数。)

尽管如此,注意释放资源是获取的好习惯,所以我可能会写FileParser类以这种方式:

class FileParser: 
    def __init__(self, file_or_filename, closing=False): 
     if hasattr(file_or_filename, 'read'): 
      self.f = file_or_filename 
      self._need_to_close = closing 
     else: 
      self.f = open(file_or_filename, 'rb') 
      self._need_to_close = True 

    def close(self): 
     if self._need_to_close: 
      self.f.close() 
      self._need_to_close = False 

然后BatchImporter。解析器将成为

def parsers(self): 
     for file in self.files: 
      yield FileParser(file) 

,或者,如果你喜欢函数式编程

def parsers(self): 
     return itertools.imap(FileParser, self.files) 

旁白:如果你是新来的Python,我建议你看看Python style guide(又称PEP 8 )。双空间缩进看起来很奇怪。

+0

如果我更改为在'with()语句(而不是try/except块)中打包'文件打开/使用/关闭代码',这会使它更安全吗?你是否只是指在产出被允许关闭打开文件句柄之前程序意外退出的可能性?顺便说一句,感谢您的洞察力 - 'file_or_filename'业务正在我的代码中,现在。 – mikewaters 2010-07-19 22:21:40