【问题标题】:Avoiding try-except nesting避免 try-except 嵌套
【发布时间】:2014-08-23 17:40:39
【问题描述】:

给定一个未知文件类型的文件,我想使用多个处理程序之一打开该文件。如果无法打开文件,每个处理程序都会引发异常。 我想尝试所有方法,如果没有成功,请引发异常。

我想出的设计是

filename = 'something.something'
try:
    content = open_data_file(filename)
    handle_data_content(content)
except IOError:
    try:
        content = open_sound_file(filename)
        handle_sound_content(content)
    except IOError:
        try:
            content = open_image_file(filename)
            handle_image_content(content)
        except IOError:
            ...

这种级联似乎不是正确的方法。

有什么建议吗?

【问题讨论】:

  • 我会推荐 codereview.stackoverflow.com
  • 在尝试处理文件之前,有没有办法检查文件的类型?也许是幻数?
  • 使用with会删除一两层try/except
  • for opener, handler in [(open_data_file, handle_data_content), ...]:?

标签: python exception-handling nested try-catch


【解决方案1】:

也许您可以将所有处理程序分组并在for 循环中评估它们,如果没有成功则在最后引发异常。您还可以继续关注引发的异常以从中获取一些信息,如下所示:

filename = 'something.something'
handlers = [(open_data_file, handle_data_context), 
            (open_sound_file, handle_sound_content),
            (open_image_file, handle_image_content)
]
for o, h in handlers:
    try:
        o(filename)
        h(filename)
        break
    except IOError as e:
        pass
else:
    # Raise the exception we got within. Also saves sub-class information.
    raise e

【讨论】:

  • for/else 将是使用succeed 做您正在做的事情的更好方法。
  • 看来是真的,我去看看。我不经常使用 for/else。感谢您的评论。
  • 值得注意的是:这不会保留所有异常信息;只是循环中引发的最后一个异常。由于异常是由不正确匹配的处理程序引发的,因此在else 子句中引发更明确的异常可能更有意义;例如IOError('File cannot be read by any known handler').
  • 是的,我同意,无论如何这并不是问题的主要部分。我宁愿让raise 部分打开。还要感谢 @pydsigner 的编辑和建议。
  • 我最近还注意到,在 Python 3 中,这段代码也应该显示原始回溯,完美地再现了原始错误消息。
【解决方案2】:

检查完全不可能吗?

>>> import urllib
>>> from mimetypes import MimeTypes

>>> guess = MimeTypes()
>>> path = urllib.pathname2url(target_file)
>>> opener = guess.guess_type(path)
>>> opener
('audio/ogg', None)

我知道try/excepteafp 在 Python 中非常流行,但有时愚蠢的一致性只会干扰手头的任务。

此外,IMO 的 try/except 循环不一定会因为您所期望的原因而中断,并且正如其他人指出的那样,如果您想查看实际发生的情况,您需要以有意义的方式报告错误您尝试迭代文件打开器,直到成功或失败。无论哪种方式,都会编写内省代码:深入尝试/异常并获得有意义的代码,或者读取文件路径并使用类型检查器,或者甚至只是拆分文件名以获得扩展名...in the face of ambiguity, refuse the temptation to guess .

【讨论】:

    【解决方案3】:

    和其他人一样,我也建议使用循环,但try/except 范围更窄。

    另外,最好重新引发原始异常,以保留有关失败的额外信息,包括回溯。

    openers_handlers = [ (open_data_file, handle_data_context) ]
    
    def open_and_handle(filename):
      for i, (opener, handler) in enumerate(openers_handlers):
        try:
            f = opener(filename)
        except IOError:
            if i >= len(openers_handlers) - 1:
                # all failed. re-raise the original exception
                raise
            else:
                # try next
                continue
        else:
            # successfully opened. handle:
            return handler(f)
    

    【讨论】:

      【解决方案4】:

      您可以使用上下文管理器:

      class ContextManager(object):
          def __init__(self, x, failure_handling): 
              self.x = x 
              self.failure_handling  = failure_handling
      
          def __enter__(self):
              return self.x
      
          def __exit__(self, exctype, excinst, exctb):
              if exctype == IOError:
                  if self.failure_handling:
                      fn = self.failure_handling.pop(0)
                      with ContextManager(fn(filename), self.failure_handling) as context:
                           handle_data_content(context)
      
              return True
      
      filename = 'something.something'
      with ContextManager(open_data_file(filename), [open_sound_file, open_image_file]) as content:
      handle_data_content(content)
      

      【讨论】:

        猜你喜欢
        • 2020-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-21
        • 2013-09-01
        • 2011-12-09
        • 2020-08-30
        • 1970-01-01
        相关资源
        最近更新 更多