简体   繁体   English

避免尝试 - 除了嵌套

[英]Avoiding try-except nesting

Given a file of unknown file type, I'd like to open that file with one of a number of handlers. 给定一个未知文件类型的文件,我想用多个处理程序之一打开该文件。 Each of the handlers raises an exception if it cannot open the file. 如果无法打开文件,则每个处理程序都会引发异常。 I would like to try them all and if none succeeds, raise an exception. 我想尝试所有这些,如果没有成功,提出异常。

The design I came up with is 我想出的设计是

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:
            ...

This cascade doesn't seem to be the right way to do it. 这种级联似乎不是正确的方法。

Any suggestions? 有什么建议?

Maybe you can group all the handlers and evaluate them in a for loop raising an exception at the end if none succeeded. 也许您可以对所有处理程序进行分组并在for循环中对它们进行评估,如果没有成功则在最后引发异常。 You can also hang on to the raised exception to get some of the information back out of it as shown here: 您还可以继续查看引发的异常,从中获取一些信息,如下所示:

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

Is checking entirely out of the question? 完全没有问题吗?

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

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

I know try/except and eafp is really popular in Python, but there are times when a foolish consistency will only interfere with the task at hand. 我知道try/excepteafp在Python中非常流行,但有时候愚蠢的一致性只会干扰手头的任务。

Additionally, IMO a try/except loop may not necessarily break for the reasons you expect, and as others have pointed out you're going to need to report the errors in a meaningful way if you want to see what's really happening as you try to iterate over file openers until you either succeed or fail. 此外,IMO的try / except循环可能不一定会因为您期望的原因而中断,并且正如其他人指出的那样,如果您想要查看实际发生的事情,您将需要以有意义的方式报告错误迭代文件切换器,直到您成功或失败。 Either way, there's introspective code being written: to dive into the try/excepts and get a meaningful one, or reading the file path and using a type checker, or even just splitting the file name to get the extension... in the face of ambiguity, refuse the temptation to guess . 无论哪种方式,都会编写内省的代码:深入了解try / excepts并获得有意义的代码,或者阅读文件路径并使用类型检查器,甚至只是拆分文件名以获得扩展...... in the face of ambiguity, refuse the temptation to guess

Like the others, I also recommend using a loop, but with tighter try/except scopes. 与其他人一样,我也建议使用循环,但使用更严格的try/except范围。

Plus, it's always better to re-raise the original exception, in order to preserve extra info about the failure, including traceback. 此外,重新提升原始异常总是更好,以便保留有关失败的额外信息,包括追溯。

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)

You can use Context Managers: 您可以使用上下文管理器:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM