简体   繁体   English

嵌套 try-except 块会导致代码过度缩进

[英]Nesting try-except blocks leads to overly indented code

I have a rather philosophical problem writing Python code.我在编写 Python 代码时遇到了一个相当哲学的问题。 Normally it is considered good style to use as many try-except-else blocks as necessary to handle all expectable cases.通常,使用尽可能多的try-except-else块来处理所有可预期的情况被认为是一种很好的风格。 But I find that this is approach sometimes leads to unnecessarily many levels of indentation that makes it difficult to keep the line count below 80 and suggest nonexistent hierarchical structure in the code.但我发现这种方法有时会导致不必要的多级缩进,这使得很难将行数保持在 80 以下,并暗示代码中不存在层次结构。 I feel I'm missing some sort of "GOTO" statement or similar jump to cope with this.我觉得我缺少某种“GOTO”语句或类似的跳转来应对这个问题。 Therefore I'm wondering if I'm missing some coding style, or Python statement, or pattern, to improve my Python code.因此,我想知道我是否缺少一些编码风格、Python 语句或模式来改进我的 Python 代码。

I'll put a more concrete example.我举一个更具体的例子。 Let's think I'm defining a function, and I'm within a loop, where various things could go wrong.让我们认为我正在定义一个函数,并且我在一个循环中,在那里各种事情可能会出错。 Each thing is sufficient to make the rest of the code within the loop skippable, which I normally skip with continue in a way similar to:每件事都足以使循环中的其余代码可跳过,我通常以类似于以下方式continue跳过这些代码:

def mu_func()
    failed = False
    for i in range(100):
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #1")
            failed = True
            continue
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #2")
            failed = True
            continue
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #3")
            failed = True
    # some cleanup code at the end of the function
    # that depends on the errors raised
    return failed

The important thing to note here is that since we are within a loop, the continue statement saves me from having to use an else statement to skip the rest of the code if any exception is raised.这里要注意的重要一点是,由于我们处于循环中,如果引发任何异常, continue语句使我不必使用else语句跳过其余代码。 Therefore the indentation level does not keep growing.因此,缩进级别不会继续增长。 Also note that I cannot just exit from the exception handling, because I need to do some cleanup right before returning from the function.另请注意,我不能只是退出异常处理,因为我需要在从函数返回之前进行一些清理。

Now note what happens when we are not within a loop :现在注意当我们不在循环中时会发生什么:

def mu_func()
    failed = False
    try:
        pass
        # we try something that might raise an exception
    except:
        print("something went wrong by reason #1")
        failed = True
    else:
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #2")
            failed = True
        else:
            try:
                pass
                # we try something that might raise an exception
            except:
                print("something went wrong by reason #3")
               failed = True
    # some cleanup code at the end of the function
    # that depends on the errors raised
    return failed

¿See the difference?看到不同? If want to skip the rest of the code after any exception has been raised, I have to put inside an else statement everything below, which trivially increases a level of indent from that point on.如果想在引发任何异常后跳过其余代码,我必须将下面的所有内容放入else语句中,从那时起,它会略微增加缩进级别。 This not only leads to annoying code where it is difficult to to keep the line count below 80 characters.这不仅会导致难以将行数保持在 80 个字符以下的烦人代码。 The worst aspect is that these nested try-except-else blocks suggest some sort of hierarchy in the code, which is artificial.最糟糕的是,这些嵌套的try-except-else块暗示了代码中的某种层次结构,这是人为的。 All exceptions are equally important.所有例外都同等重要。 This can be easily appreciated in the first example where the order of the try-except blocks could be nicely exchanged without touching the indent and without affecting the functionality.在第一个示例中可以很容易地理解这一点,其中可以很好地交换try-except块的顺序,而无需触及缩进且不影响功能。

Is there a Python statement, or pattern, that allows me to skip the rest of the code once an exception is handled, that precludes me from having to add more and more indent levels?是否有 Python 语句或模式允许我在处理异常后跳过其余代码,从而使我不必添加越来越多的缩进级别? Am I misusing the else statement?我是否误用了else语句? I'm tempted to put everything within a trivial 1-iteration loop so I have access to the continue statement to perform the jump to the end of block statement that I'm missing.我很想把所有东西都放在一个简单的 1 次迭代循环中,这样我就可以访问continue语句来执行跳转到我遗漏的块语句的末尾

One approach might be to wrap the for loop with a try-except block:一种方法可能是用try-except块包装 for 循环:

def mu_func():
    try:
        for i in range(100):
            # we try something that might raise an exception
            action_1()
            print("action #1 succeeds")

            action_2()
            print("action #2 succeeds")

            action_3()
            print("action #3 succeeds")

    except:
        print("something went wrong!")
        failed = True

    else:
        failed = False

    return failed

If you have other logic in your for loop, I'd suggest wrapping only the logic that might raise an exception:如果你的 for 循环中有其他逻辑,我建议只包装可能引发异常的逻辑:

def mu_func():
    for i in range(100):
        try:
            # we try something that might raise an exception
            action_1()
            print("action #1 succeeds")

            action_2()
            print("action #2 succeeds")

            action_3()
            print("action #3 succeeds")

        except:
            print("something went wrong!")
            failed = True
            break

        # here we might do other stuff in the loop
        ...
    
    else:
        failed = False

    return failed

What about using:怎么用:

def mu_func()
    failed = False
    try:
        pass
        # we try something that might raise an exception
    except ExceptionClass1():
        print("something went wrong by reason #1")
        failed = True
    except ExceptionClass2():
        print("something went wrong by reason #2")
        failed = True
    except ExceptionClass3():
        print("something went wrong by reason #3")
        failed = True
    except ExceptionClass4():
        print("something went wrong by reason #4")
        failed = True
    return failed

I have found 2 solutions to the problem.我找到了 2 个解决问题的方法。 I'll post them here to either stimulate further discussion or help others in my situation.我会将它们张贴在这里以激发进一步的讨论或在我的情况下帮助其他人。 Still, I won't mark this as the answer because I find them more like a workaround than a proper solution.不过,我不会将此标记为答案,因为我发现它们更像是一种解决方法而不是正确的解决方案。 They are to some extent based on the answers to this question (whose accepted answer I do not think it solves the problem).它们在某种程度上是基于这个问题的答案(我认为它接受的答案不能解决问题)。

Solution using a dummy loop使用虚拟循环的解决方案

Here the idea is to fake a loop to have access to the continue statement that allows you to jump to the end of function.这里的想法是伪造一个循环来访问允许您跳转到函数末尾的continue语句。

def mu_func()
    failed = False
    for _ in range(1):
        try:
            pass
            # we try something that might raise an exception
        except Exception1:
            print("something went wrong by reason #1")
            failed = True
            continue
        try:
            pass
            # we try something that might raise an exception
        except Exception2:
            print("something went wrong by reason #2")
            failed = True
            continue
        try:
            pass
            # we try something that might raise an exception
        except Exception3:
            print("something went wrong by reason #3")
            failed = True
    # some cleanup code at the end of the function
    # that must be executed before leaving
    return failed

This solution works, but it is very ugly because uses a for loop in a creative way that is clearly not what it was designed for.这个解决方案有效,但它非常丑陋,因为以一种创造性的方式使用 for 循环,这显然不是它的设计目的。

Solution using 2-level exception handling使用2级异常处理的解决方案

Here the idea is to define and use a general exception, and use it to translate the exception type to signal that something went wrong (but it was already handled) and then basically send the execution flow to the end.这里的想法是定义和使用通用异常,并使用它来转换异常类型以表示出现问题(但它已经被处理),然后基本上将执行流程发送到最后。

class MyException(Exception):
    pass

def mu_func()
    failed = False
    try:
        try:
            pass
            # we try something that might raise an exception
        except Exception1:
            print("something went wrong by reason #1")
            raise MyException
        try:
            pass
            # we try something that might raise an exception
        except Exception2:
            print("something went wrong by reason #2")
            raise MyException
        try:
            pass
            # we try something that might raise an exception
        except Exception3:
            print("something went wrong by reason #3")
            raise MyException
    except MyException:
        failed = True
    # some cleanup code at the end of the function
    # that depends on the errors raised
    return failed

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

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