为什么“除了:通过”一个坏的编程实践? [英] Why is "except: pass" a bad programming practice?

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

问题描述

我经常看到有关如何使用的其他堆栈溢出问题的评论,但不鼓励通过。为什么这样坏?有时我只是不在乎错误,我只想继续执行代码。

  try:
something
除了:
pass

为什么要使用 except:pass block bad?什么使它坏?是否这个事实是,我通过错误,或者我除了任何错误?

解决方案

正如你所确定的那样,有两方面:通过在<$ c $之后指定没有异常类型来捕捉任何错误c>除了,只需传递它而不采取任何行动。



我的解释是有点更长 - 所以tl


  1. 不要抓住任何错误。总是指定您准备从哪些例外进行恢复,并仅捕获这些例外。

  2. 尽量避免传递除块。除非明确要求,否则通常不是一个好兆头。

但是让我们详细介绍一下:



不要抓住任何错误



使用尝试阻止,你通常这样做是因为你知道有机会抛出异常。因此,您也已经有了一个大概的想法,即 可以破坏什么异常可以抛出。在这种情况下,您会捕获异常,因为您可以从中正确恢复。这意味着你已经准备好了这个例外,并且有一些替代的计划,你将会遵循这种例外情况。



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



然而,如果我们要抓住所有的东西,那么 - 除了这些例外,我们准备从中恢复,还有一个机会,我们得到了我们没想到的异常,我们确实无法恢复或者不应该从中恢复。



我们从上面来看配置文件的例子。在缺少文件的情况下,我们只是应用了我们的默认配置,稍后可能会决定自动保存配置(因此下次文件存在)。现在想象我们得到一个 IsADirectoryError ,或 PermissionError 。在这种情况下,我们可能不想继续;我们仍然可以应用我们的默认配置,但是我们以后将无法保存文件。而且用户很可能也有自定义配置,所以可能不需要使用默认值。所以我们想立即告诉用户,也可能中止程序执行。但是,这不是我们想要在某个小代码部分深入的一部分;这是一个应用程序级别的重要性,所以它应该在顶部处理 - 所以让异常起泡。



另一个简单的例子也在 Python 2成语文档。这里,代码中存在一个简单的打字错误,导致它中断。因为我们正在捕捉每个异常,我们还抓住了 NameError s SyntaxError 取值。两者都是在编程时发生在我们身上的错误;两者都是我们绝对不想在运送代码时加入的错误。但是因为我们也抓住了这些,我们甚至不知道他们在那里发生了,失去了任何帮助来正确调试它。



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



无论如何,你不太可能为代码的一小部分,所以这只是你应该只捕捉你准备的例外。有些人建议至少抓住 例外 ,因为它不会包括像 SystemExit KeyboardInterrupt 其中设计的东西是终止您的申请,但我会认为这仍然是非常特别的。只有一个地方,我个人接受捕获异常或只是任何异常,这是在一个全局应用程序级异常处理程序中记录我们没有准备的任何异常的单一目的。这样,我们仍然可以保留关于意外异常的信息,然后我们可以使用它们来扩展我们的代码来明确地处理这些异常(如果我们可以从它们中恢复),或者在错误的情况下创建测试用例以确保它不会再发生。但是,当然,只有当我们只捕捉到我们已经期待的这些例外情况时,才有效果,所以我们没有想到的那些例外就会自然而然。



尽量避免传递除了块



当明确捕捉到一些特定异常的选择时,很多情况下我们根本就不做任何事情。在这种情况下,只要有,除了SomeSpecificException:pass 就好了。大多数时候,不是这样,因为我们可能需要一些与恢复过程相关的代码(如上所述)。这可以是例如再次重试动作或者设置默认值的东西。



如果不是这样,例如因为我们的代码已经结构重复,直到成功,然后只是传递是足够好的。以上面的例子,我们可能要求用户输入一个数字。因为我们知道用户不喜欢我们要求他们的,所以我们可能会把它放在一个循环中,所以可能如下所示:

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

因为我们继续尝试,直到没有异常抛出,我们不需要做任何特殊的除了块,所以这是罚款。但是当然可以说,我们至少想向用户显示一些错误消息,告诉他为什么他必须重复输入。



在许多其他情况下只是传递一个,除了是一个迹象,我们没有为我们正在捕捉到的异常做好准备。除非这些例外是简单的(例如 ValueError TypeError ),我们可以通过的原因是显而易见的,尝试避免刚刚过去。如果真的没有什么可做的(你绝对确定),那么考虑增加一个评论为什么会这样;否则,展开except块以实际包含一些恢复码。



除了:pass



最糟糕的罪犯是两者的结合。这意味着我们愿意捕捉任何错误,尽管我们绝对没有准备好,我们也不做任何事情。您至少要记录错误,也可能会重新启动它,以便仍然终止应用程序(不可能像MemoryError那样继续运行)。只是传递不仅会保持应用程序有些活着(取决于你在哪里捕获),而且会丢弃所有的信息,使得不可能发现错误 - 这是特别是如果你不是发现它。 / p>




所以底线是:只捕捉你真正期望的并准备从中恢复的异常;所有其他人都可能是您应该修复的错误,或者您还没有准备好的事情。如果您真的不需要做某些事情,通过特定的异常就可以了。在所有其他情况下,这只是推定的标志,并且是懒惰的。你一定要修复这个问题。


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

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?

解决方案

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.

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

  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:

Don’t catch any error

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.

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.

Let’s take the configuration file example from above. In case of a missing file, we just applied our default configuration, and might decided 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.

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.

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.

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.

Try to avoid passing in except blocks

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

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.

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.

except: pass

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 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 is 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天全站免登陆