为什么是“除外:通过"?糟糕的编程习惯? [英] Why is "except: pass" a bad programming practice?

查看:22
本文介绍了为什么是“除外:通过"?糟糕的编程习惯?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常看到其他有关如何不鼓励使用 except: pass 的 Stack Overflow 问题的评论.为什么这很糟糕?有时我只是不关心错误是什么,我只想继续编写代码.

I often see comments on other Stack Overflow questions about how the use of except: pass is discouraged. Why is this bad? Sometimes I just don't care what the errors are and I want to just continue with the code.

try:
    something
except:
    pass

为什么使用 except: pass 块不好?是什么让它变坏了?是我 pass 错误还是我 except 任何错误?

Why is using an except: pass block bad? What makes it bad? Is it the fact that I pass on an error or that I except any error?

推荐答案

正如你猜对的那样,它有两个方面: 通过在 except<之后指定无异常类型来捕获 any 错误/code>,并简单地传递它而不采取任何行动.

As you correctly guessed, there are two sides to it: Catching any error by specifying no exception type after except, and simply passing it without taking any action.

我的解释有点"长——所以 tl;dr 它分解为:

My explanation is "a bit" longer—so tl;dr it breaks down to this:

  1. 不要发现任何错误.始终指定您准备从哪些异常中恢复并仅捕获这些异常.
  2. 尽量避免传入除了块.除非明确要求,否则这通常不是一个好兆头.
  1. Don’t catch any error. Always specify which exceptions you are prepared to recover from and only catch those.
  2. Try to avoid passing in except blocks. Unless explicitly desired, this is usually not a good sign.

但让我们详细介绍一下:

But let’s go into detail:

当使用 try 块时,您通常会这样做,因为您知道有可能引发异常.因此,您也已经大致了解 什么 可以破坏以及可以引发什么异常.在这种情况下,您可以捕获异常,因为您可以积极地从中恢复.这意味着您已为例外情况做好了准备,并有一些替代计划,以防发生该例外情况.

When using a try block, you usually do this because you know that there is a chance of an exception being thrown. As such, you also already have an approximate idea of what can break and what exception can be thrown. In such cases, you catch an exception because you can positively recover from it. That means that you are prepared for the exception and have some alternative plan which you will follow in case of that exception.

例如,当您要求用户输入一个数字时,您可以使用 int() 转换输入,这可能会引发 ValueError.您可以通过简单地要求用户再次尝试来轻松恢复,因此捕获 ValueError 并再次提示用户将是一个合适的计划.一个不同的例子是,如果您想从文件中读取一些配置,而该文件恰好不存在.因为它是一个配置文件,您可能有一些默认配置作为备用,因此该文件不是必需的.因此,捕获 FileNotFoundError 并简单地应用默认配置在这里是一个不错的计划.现在,在这两种情况下,我们都有一个非常具体的例外,我们期望并且有一个同样具体的计划来从中恢复.因此,在每种情况下,我们明确地只except 那个特定的异常.

For example, when you ask for the user to input a number, you can convert the input using int() which might raise a ValueError. You can easily recover that by simply asking the user to try it again, so catching the ValueError and prompting the user again would be an appropriate plan. A different example would be if you want to read some configuration from a file, and that file happens to not exist. Because it is a configuration file, you might have some default configuration as a fallback, so the file is not exactly necessary. So catching a FileNotFoundError and simply applying the default configuration would be a good plan here. Now in both these cases, we have a very specific exception we expect and have an equally specific plan to recover from it. As such, in each case, we explicitly only except that certain exception.

但是,如果我们要捕获所有内容,那么——除了我们准备从中恢复的那些异常之外——我们也有可能得到我们没有预料到的异常,并且我们确实无法从中恢复;或者不应该从中恢复.

However, if we were to catch everything, then—in addition to those exceptions we are prepared to recover from—there is also a chance that we get exceptions that we didn’t expect, and which we indeed cannot recover from; or shouldn’t recover from.

让我们以上面的配置文件为例.如果文件丢失,我们只是应用了默认配置,稍后可能会决定自动保存配置(因此下次,该文件存在).现在假设我们得到一个 IsADirectoryError,或者PermissionError 代替.在这种情况下,我们可能不想继续;我们仍然可以应用我们的默认配置,但我们稍后将无法保存文件.并且用户可能也打算进行自定义配置,因此可能不希望使用默认值.所以我们想立即告诉用户它,并且可能也中止程序执行.但这不是我们想要在一些小的代码部分深处做的事情.这是应用程序级别的重要内容,因此应该在顶部处理——所以让异常冒泡吧.

Let’s take the configuration file example from above. In case of a missing file, we just applied our default configuration and might decide at a later point to automatically save the configuration (so next time, the file exists). Now imagine we get a IsADirectoryError, or a PermissionError instead. In such cases, we probably do not want to continue; we could still apply our default configuration, but we later won’t be able to save the file. And it’s likely that the user meant to have a custom configuration too, so using the default values is likely not desired. So we would want to tell the user about it immediately, and probably abort the program execution too. But that’s not something we want to do somewhere deep within some small code part; this is something of application-level importance, so it should be handled at the top—so let the exception bubble up.

Python 2 idioms 文档中还提到了另一个简单示例.在这里,代码中存在一个简单的错字导致它中断.因为我们要捕获每一个异常,我们也捕获NameErrorsSyntaxErrors.两者都是我们在编程时都会发生的错误,而且都是我们在发布代码时绝对不想包含的错误.但是因为我们也捕获了那些,我们甚至不会知道它们发生在那里,并且失去了正确调试它的任何帮助.

Another simple example is also mentioned in the Python 2 idioms document. Here, a simple typo exists in the code which causes it to break. Because we are catching every exception, we also catch NameErrors and SyntaxErrors. Both are mistakes that happen to us all while programming and both are mistakes we absolutely don’t want to include when shipping the code. But because we also caught those, we won’t even know that they occurred there and lose any help to debug it correctly.

但也有更危险的例外情况,我们不太可能做好准备.例如,SystemError 通常是很少发生的事情,我们不能真正做到计划;这意味着发生了一些更复杂的事情,可能会阻止我们继续当前的任务.

But there are also more dangerous exceptions which we are unlikely prepared for. For example, SystemError is usually something that happens rarely and which we cannot really plan for; it means there is something more complicated going on, something that likely prevents us from continuing the current task.

在任何情况下,您都不太可能在代码的一小部分中为所有事情做好准备,因此您实际上应该只捕获您准备好的那些异常.有人建议至少捕获 Exception因为它不包括像 SystemExitKeyboardInterrupt 这样设计要终止您的应用程序的东西,但我认为这还很远太不具体了.只有一个地方我个人接受捕获 Exception 或只是 any 异常,那就是在单个全局应用程序级异常处理程序中,其目的是记录任何我们没有准备好例外.这样,我们仍然可以保留尽可能多的有关意外异常的信息,然后我们可以使用这些信息来扩展我们的代码以显式处理这些异常(如果我们可以从它们中恢复),或者在出现错误的情况下创建测试用例以确保它不会再发生了.但是,当然,这只有在我们只捕获那些我们已经预料到的异常时才有效,所以我们没有预料到的那些自然会冒泡.

In any case, it’s very unlikely that you are prepared for everything in a small-scale part of the code, so that’s really where you should only catch those exceptions you are prepared for. Some people suggest to at least catch Exception as it won’t include things like SystemExit and KeyboardInterrupt which by design are to terminate your application, but I would argue that this is still far too unspecific. There is only one place where I personally accept catching Exception or just any exception, and that is in a single global application-level exception handler which has the single purpose to log any exception we were not prepared for. That way, we can still retain as much information about unexpected exceptions, which we then can use to extend our code to handle those explicitly (if we can recover from them) or—in case of a bug—to create test cases to make sure it won’t happen again. But of course, that only works if we only ever caught those exceptions we were already expecting, so the ones we didn’t expect will naturally bubble up.

当显式捕获一小部分特定异常时,在很多情况下我们什么都不做就可以了.在这种情况下,只需 except SomeSpecificException: pass 就可以了.但大多数时候,情况并非如此,因为我们可能需要一些与恢复过程相关的代码(如上所述).例如,这可以是再次重试操作,或者改为设置默认值.

When explicitly catching a small selection of specific exceptions, there are many situations in which we will be fine by simply doing nothing. In such cases, just having except SomeSpecificException: pass is just fine. Most of the time though, this is not the case as we likely need some code related to the recovery process (as mentioned above). This can be for example something that retries the action again, or to set up a default value instead.

如果不是这样,例如,因为我们的代码已经构造为重复直到成功,那么传递就足够了.以上面的示例为例,我们可能希望要求用户输入一个数字.因为我们知道用户喜欢不做我们要求他们做的事情,所以我们可能一开始就把它放入一个循环中,所以它可能看起来像这样:

If that’s not the case though, for example, because our code is already structured to repeat until it succeeds, then just passing is good enough. Taking our example from above, we might want to ask the user to enter a number. Because we know that users like to not do what we ask them for, we might just put it into a loop in the first place, so it could look like this:

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

因为我们一直在尝试直到没有抛出异常,所以我们不需要在 except 块中做任何特殊的事情,所以这很好.但当然,有人可能会争辩说,我们至少想向用户显示一些错误消息,告诉他为什么他必须重复输入.

Because we keep trying until no exception is thrown, we don’t need to do anything special in the except block, so this is fine. But of course, one might argue that we at least want to show the user some error message to tell him why he has to repeat the input.

不过,在许多其他情况下,仅传递 except 表明我们并未真正为捕获的异常做好准备.除非这些异常很简单(如 ValueErrorTypeError),而且我们可以通过的原因很明显,否则尽量避免只是通过.如果真的无事可做(并且您对此非常确定),请考虑添加评论为什么会这样;否则,扩展 except 块以实际包含一些恢复代码.

In many other cases though, just passing in an except is a sign that we weren’t really prepared for the exception we are catching. Unless those exceptions are simple (like ValueError or TypeError), and the reason why we can pass is obvious, try to avoid just passing. If there’s really nothing to do (and you are absolutely sure about it), then consider adding a comment why that’s the case; otherwise, expand the except block to actually include some recovery code.

不过,最严重的违规者是两者的结合.这意味着我们愿意捕获任何错误,尽管我们绝对没有为此做好准备并且我们也不会对此采取任何措施.您至少想要记录错误并可能重新引发它以仍然终止应用程序(在 MemoryError 之后您不太可能像往常一样继续).只是通过,不仅会使应用程序保持活跃(当然取决于您捕获的位置),而且还会丢弃所有信息,从而无法发现错误 - 如果您不是发现错误的人,尤其如此.

The worst offender though is the combination of both. This means that we are willingly catching any error although we are absolutely not prepared for it and we also don’t do anything about it. You at least want to log the error and also likely reraise it to still terminate the application (it’s unlikely you can continue like normal after a MemoryError). Just passing though will not only keep the application somewhat alive (depending on where you catch of course), but also throw away all the information, making it impossible to discover the error—which is especially true if you are not the one discovering it.

所以底线是:只捕获您真正期望并准备从中恢复的异常;所有其他人可能要么是您应该修复的错误,要么是您无论如何都没有准备好的事情.如果您真的不需要对它们做任何事情,则传递 特定 异常是可以的.在所有其他情况下,这只是假设和懒惰的标志.你肯定想解决这个问题.

So the bottom line is: Catch only exceptions you really expect and are prepared to recover from; all others are likely either mistakes you should fix or something you are not prepared for anyway. Passing specific exceptions are fine if you really don’t need to do something about them. In all other cases, it’s just a sign of presumption and being lazy. And you definitely want to fix that.

这篇关于为什么是“除外:通过"?糟糕的编程习惯?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆