为什么“ except:pass”是不好的编程习惯?

问题:为什么“ except:pass”是不好的编程习惯?

我经常看到有关except: pass不鼓励使用的其他Stack Overflow问题的评论。为什么这样不好?有时我只是不在乎错误是什么,我只想继续编写代码。

try:
    something
except:
    pass

为什么使用except: pass积木不好?是什么让它不好?是我pass出错还是我except出错了?

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?


回答 0

正如您正确猜测的那样,它有两个方面:通过在之后不指定任何异常类型来捕获任何错误except,并在不采取任何操作的情况下简单地传递它。

我的解释是“更长”的时间,所以tl; dr可以细分为:

  1. 不要发现任何错误。始终指定您准备从中恢复的异常,并且仅捕获这些异常。
  2. 尽量避免传入除了blocks。除非明确要求,否则通常不是一个好兆头。

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

不要发现任何错误

使用try块时,通常这样做是因为您知道有可能引发异常。这样,您还已经大概知道了哪些会中断,哪些异常会引发。在这种情况下,您会捕获异常,因为您可以从中积极地恢复过来。这意味着您已为exceptions做好了准备,并有一些替代计划,在发生这种exceptions时将遵循该计划。

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

但是,如果我们要抓住一切,那么除了准备好从那些异常中恢复过来,我们还有机会获得我们没有想到的异常,而我们确实无法从中恢复。或不应从中恢复。

让我们以上面的配置文件示例为例。如果文件丢失,我们将应用默认配置,并可能在以后决定自动保存配置(因此下次该文件存在)。现在想象我们得到一个IsADirectoryError或一个PermissionError代替。在这种情况下,我们可能不想继续。我们仍然可以应用默认配置,但是以后将无法保存文件。而且用户可能也打算具有自定义配置,因此可能不需要使用默认值。因此,我们希望立即将其告知用户,并且可能也中止程序执行。但这不是我们想要在某些小代码部分的深处做的事情。这在应用程序级别上很重要,因此应该在顶部进行处理-因此让异常冒出来。

Python 2习惯用法文档中还提到了另一个简单的示例。在这里,代码中存在一个简单的错字,导致它中断。因为我们正在捕获每个异常,所以我们也捕获了NameErrorsSyntaxErrors。两者都是编程时我们所有人都会遇到的错误。两者都是我们在交付代码时绝对不希望包含的错误。但是,因为我们也抓住了它们,所以我们甚至都不知道它们在那里发生,并且失去了正确调试它的任何帮助。

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

无论如何,您几乎不可能为代码中的一小部分做好一切准备,因此,实际上,您应该只捕获准备好的那些异常。有人建议至少要赶上Exception它,因为它不会包含类似的内容,SystemExitKeyboardInterrupt这些内容在设计上是要终止您的应用程序的,但是我认为这仍然过于不确定。我个人只在一个地方接受捕捞活动,Exception或者在任何地方异常,并且在单个全局应用程序级异常处理程序中,该异常处理程序的唯一目的是记录我们没有准备好的任何异常。这样,我们仍然可以保留有关意外异常的尽可能多的信息,然后我们可以使用这些信息来扩展代码以显式处理这些异常(如果可以从异常中恢复),或者在发生错误的情况下创建测试用例以确保它不会再发生。但是,当然,只有当我们只捕获到我们已经期望的异常时,这才起作用,所以我们没有想到的异常自然会冒出来。

尽量避免传入除了块

当显式地捕获少量特定异常时,在许多情况下,只要不执行任何操作就可以了。在这种情况下,拥有except SomeSpecificException: pass就好。不过,在大多数情况下,情况并非如此,因为我们可能需要一些与恢复过程相关的代码(如上所述)。例如,这可以是重试该操作的内容,也可以是设置默认值的内容。

但是,如果不是这种情况,例如因为我们的代码已经被构造为可以重复执行直到成功,那么传递就足够了。从上面的例子中,我们可能想要求用户输入一个数字。因为我们知道用户不想按照我们的要求去做,所以我们可能首先将其放入循环中,因此看起来可能像这样:

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

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

但是,在许多其他情况下,仅传递except一个信号就表明我们并未真正为所捕获的异常做好准备。除非这些异常很简单(如ValueErrorTypeError),并且我们可以通过的原因很明显,否则请尝试避免仅通过。如果真的无事可做(您对此绝对有把握),则考虑添加评论,为什么会这样;否则,展开except块以实际包括一些恢复代码。

except: pass

不过,最严重的罪犯是两者的结合。这意味着我们乐于捕捉任何错误,尽管我们绝对没有为此做好准备,并且我们也不对此做任何事情。您至少要记录该错误,还可能重新引发该错误以仍然终止应用程序(在出现MemoryError后,您不太可能像往常一样继续操作)。只是传递信息不仅可以使应用程序保持一定的生命力(当然,还取决于您捕获的位置),而且还会丢弃所有信息,从而无法发现错误-如果您不是发现错误的人,则尤其如此。


因此,底线是:仅捕获您真正期望并准备从中恢复的异常;其他所有问题都可能是您应纠正的错误,或者您没有准备好应对。如果您真的不需要对异常进行处理,则传递特定的异常很好。在其他所有情况下,这只是推定和懒惰的标志。您肯定想解决该问题。

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.


回答 1

这里的主要问题是它会忽略所有错误:内存不足,CPU正在燃烧,用户想要停止,程序想要退出,Jabberwocky正在杀死用户。

这太多了。在您的脑海中,您正在思考“我想忽略此网络错误”。如果出乎意料的地方出了问题,那么您的代码将以无人能及的方式以无法预测的方式静默继续并中断。

这就是为什么您应该将自己限制为仅忽略某些错误,而让其余错误通过。

The main problem here is that it ignores all and any error: Out of memory, CPU is burning, user wants to stop, program wants to exit, Jabberwocky is killing users.

This is way too much. In your head, you’re thinking “I want to ignore this network error”. If something unexpected goes wrong, then your code silently continues and breaks in completely unpredictable ways that no one can debug.

That’s why you should limit yourself to ignoring specifically only some errors and let the rest pass.


回答 2

从字面上执行伪代码甚至不会给出任何错误:

try:
    something
except:
    pass

就像是一段完全有效的代码,而不是抛出NameError。我希望这不是您想要的。

Executing your pseudo code literally does not even give any error:

try:
    something
except:
    pass

as if it is a perfectly valid piece of code, instead of throwing a NameError. I hope this is not what you want.


回答 3

为什么“ except:pass”是不好的编程习惯?

为什么这样不好?

try:
    something
except:
    pass

这会捕获所有可能的异常,包括GeneratorExitKeyboardInterruptSystemExit-这是您可能不打算捕获的异常。和赶上一样BaseException

try:
    something
except BaseException:
    pass

版本的文档说

由于Python中的每个错误都会引发一个异常,因此使用except:可能会使许多编程错误看起来像运行时问题,从而阻碍了调试过程。

Python异常层次结构

如果捕获父异常类,那么还将捕获其所有子类。仅捕获您准备处理的异常要优雅得多。

这是Python 3 异常层次结构 -您是否真的想抓住一切?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

不要这样

如果您使用这种形式的异常处理:

try:
    something
except: # don't just do a bare except!
    pass

这样,您将无法something使用Ctrl-C 中断您的代码块。您的程序将忽略try代码块内的所有可能的Exception 。

这是另一个具有相同不良行为的示例:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

相反,请尝试仅捕获您要查找的特定异常。例如,如果您知道转换可能会产生价值错误:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

另一个示例的进一步说明

您之所以这样做,是因为您一直在爬网并说a UnicodeError,但是由于使用了最广泛的Exception catch,您的代码(可能有其他基本缺陷)将尝试运行至完成,浪费带宽,处理时间,设备的磨损,内存不足,收集垃圾数据等。

如果其他人要求您完成操作,以便他们可以依靠您的代码,那么我理解被迫仅处理所有事情。但是,如果您愿意在开发过程中大声失败,那么您将有机会纠正可能会间歇性出现的问题,但这将是长期的代价高昂的错误。

通过更精确的错误处理,您的代码可以更强大。

Why is “except: pass” a bad programming practice?

Why is this bad?

try:
    something
except:
    pass

This catches every possible exception, including GeneratorExit, KeyboardInterrupt, and SystemExit – which are exceptions you probably don’t intend to catch. It’s the same as catching BaseException.

try:
    something
except BaseException:
    pass

Older versions of the documentation say:

Since every error in Python raises an exception, using except: can make many programming errors look like runtime problems, which hinders the debugging process.

Python Exception Hierarchy

If you catch a parent exception class, you also catch all of their child classes. It is much more elegant to only catch the exceptions you are prepared to handle.

Here’s the Python 3 exception hierarchy – do you really want to catch ’em all?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

Don’t Do this

If you’re using this form of exception handling:

try:
    something
except: # don't just do a bare except!
    pass

Then you won’t be able to interrupt your something block with Ctrl-C. Your program will overlook every possible Exception inside the try code block.

Here’s another example that will have the same undesirable behavior:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

Instead, try to only catch the specific exception you know you’re looking for. For example, if you know you might get a value-error on a conversion:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

Further Explanation with another example

You might be doing it because you’ve been web-scraping and been getting say, a UnicodeError, but because you’ve used the broadest Exception catching, your code, which may have other fundamental flaws, will attempt to run to completion, wasting bandwidth, processing time, wear and tear on your equipment, running out of memory, collecting garbage data, etc.

If other people are asking you to complete so that they can rely on your code, I understand feeling compelled to just handle everything. But if you’re willing to fail noisily as you develop, you will have the opportunity to correct problems that might only pop up intermittently, but that would be long term costly bugs.

With more precise error handling, you code can be more robust.


回答 4

>>> import this

提姆·彼得斯(Tim Peters)撰写的《 Python之禅》

美丽胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套更好。
稀疏胜于密集。
可读性很重要。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误绝不能默默传递。
除非明确地保持沉默。
面对模棱两可的想法,拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。
尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
现在总比没有好。
虽然从来没有比这更好正确的现在。
如果实现难以解释,那是个坏主意。
如果实现易于解释,则可能是个好主意。
命名空间是一个很棒的主意-让我们做更多这些吧!

所以,这是我的看法。每当发现错误时,都应该采取措施进行处理,即将其写入日志文件或其他内容。至少,它通知您以前曾经有错误。

>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!

So, here is my opinion. Whenever you find an error, you should do something to handle it, i.e. write it in logfile or something else. At least, it informs you that there used to be a error.


回答 5

您至少except Exception:应避免捕获诸如SystemExit或的系统异常KeyboardInterrupt。这里是文档链接

通常,应明确定义要捕获的异常,以避免捕获不需要的异常。您应该知道忽略了哪些异常。

You should use at least except Exception: to avoid catching system exceptions like SystemExit or KeyboardInterrupt. Here’s link to docs.

In general you should define explicitly exceptions you want to catch, to avoid catching unwanted exceptions. You should know what exceptions you ignore.


回答 6

首先,它违反了Python Zen的两个原则:

  • 显式胜于隐式
  • 错误绝不能默默传递

这意味着您故意使错误静默地通过。而且,您不知道确切发生了哪个错误,因为except: pass它将捕获任何异常。

其次,如果我们试图从Python的Zen中抽象出来,并以理智的眼光说话,您应该知道,使用except:pass会使您在系统中没有知识和控制力。经验法则是在发生错误时引发异常,并采取适当的措施。如果您事先不知道该怎么做,请至少将错误记录在某个地方(并最好重新引发该异常):

try:
    something
except:
    logger.exception('Something happened')

但是,通常,如果您尝试捕获任何异常,则可能是在做错什么!

First, it violates two principles of Zen of Python:

  • Explicit is better than implicit
  • Errors should never pass silently

What it means, is that you intentionally make your error pass silently. Moreover, you don’t event know, which error exactly occurred, because except: pass will catch any exception.

Second, if we try to abstract away from the Zen of Python, and speak in term of just sanity, you should know, that using except:pass leaves you with no knowledge and control in your system. The rule of thumb is to raise an exception, if error happens, and take appropriate actions. If you don’t know in advance, what actions these should be, at least log the error somewhere (and better re-raise the exception):

try:
    something
except:
    logger.exception('Something happened')

But, usually, if you try to catch any exception, you are probably doing something wrong!


回答 7

except:pass构造实质上使在代码中涵盖的代码出现时出现的所有异常情况保持沉默。try:在运行块中。

造成这种不良习惯的原因在于,通常这并不是您真正想要的。更常见的情况是,您想沉默一些特定的情况,并且except:pass这实在是一种过时的工具。它可以完成工作,但也可以掩盖您可能未曾预料到的其他错误情况,但很可能希望以其他方式处理。

在Python中这一点尤其重要的是,通过这种语言的习惯用法,异常不一定是error。当然,就像大多数语言一样,通常也以这种方式使用它们。但是特别是Python偶尔会使用它们来实现一些代码任务的替代退出路径,这实际上并不是正常运行情况的一部分,但仍然不时出现,并且在大多数情况下甚至可以预期。SystemExit已经作为一个旧示例被提及,但是如今最常见的示例可能是StopIteration。这种使用异常的方式引起了很多争议,尤其是在迭代器和生成器首次引入Python时,但最终这个想法盛行。

The except:pass construct essentially silences any and all exceptional conditions that come up while the code covered in the try: block is being run.

What makes this bad practice is that it usually isn’t what you really want. More often, some specific condition is coming up that you want to silence, and except:pass is too much of a blunt instrument. It will get the job done, but it will also mask other error conditions that you likely haven’t anticipated, but may very well want to deal with in some other way.

What makes this particularly important in Python is that by the idioms of this language, exceptions are not necessarily errors. They’re often used this way, of course, just as in most languages. But Python in particular has occasionally used them to implement an alternative exit path from some code tasks which isn’t really part of the normal running case, but is still known to come up from time to time and may even be expected in most cases. SystemExit has already been mentioned as an old example, but the most common example nowadays may be StopIteration. Using exceptions this way caused a lot of controversy, especially when iterators and generators were first introduced to Python, but eventually the idea prevailed.


回答 8

已经说明了#1原因-它隐藏了您没有想到的错误。

(#2)- 它使您的代码难以被他人阅读和理解。如果在尝试读取文件时捕获到FileNotFoundException,那么对于另一个开发人员而言,“ catch”块应具有的功能非常明显。如果未指定异常,则需要附加注释以说明该块应执行的操作。

(#3)- 演示了惰性编程。如果使用通用的try / catch,则表明您不了解程序中可能出现的运行时错误,或者您不知道Python中可能出现的异常。捕获特定错误表明您既了解程序又了解Python引发的错误范围。这更有可能使其他开发人员和代码审阅者信任您的工作。

The #1 reason has already been stated – it hides errors that you did not expect.

(#2) – It makes your code difficult for others to read and understand. If you catch a FileNotFoundException when you are trying to read a file, then it is pretty obvious to another developer what functionality the ‘catch’ block should have. If you do not specify an exception, then you need additional commenting to explain what the block should do.

(#3) – It demonstrates lazy programming. If you use the generic try/catch, it indicates either that you do not understand the possible run-time errors in your program, or that you do not know what exceptions are possible in Python. Catching a specific error shows that you understand both your program and the range of errors that Python throws. This is more likely to make other developers and code-reviewers trust your work.


回答 9

那么,此代码产生什么输出?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

现在,假设tryexcept块是对复杂对象层次结构的数百行调用,它本身在大型程序的调用树中间被调用。当程序出错时,您从哪里开始寻找?

So, what output does this code produce?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

Now imagine the tryexcept block is hundreds of lines of calls to a complex object hierarchy, and is itself called in the middle of large program’s call tree. When the program goes wrong, where do you start looking?


回答 10

通常,您可以将任何错误/异常分为以下三种类别之一

  • 致命的:不是您的错,您无法阻止它们,也无法从中恢复。您当然不应该忽略它们并继续运行,并使程序保持未知状态。只要让错误终止您的程序,您就无能为力了。

  • 骨头:您自己的错误,很可能是由于疏忽,错误或编程错误所致。您应该修复该错误。同样,您当然应该不忽略并继续。

  • 外生的:在特殊情况下(例如找不到文件连接终止),您可能会遇到这些错误。您应该明确地处理这些错误,并且仅处理这些错误。

在任何情况下,except: pass都只会使程序处于未知状态,在这种状态下可能会造成更大的破坏。

In general, you can classify any error/exception in one of three categories:

  • Fatal: Not your fault, you cannot prevent them, you cannot recover from them. You should certainly not ignore them and continue, and leave your program in an unknown state. Just let the error terminate your program, there is nothing you can do.

  • Boneheaded: Your own fault, most likely due to an oversight, bug or programming error. You should fix the bug. Again, you should most certainly not ignore and continue.

  • Exogenous: You can expect these errors in exceptional situations, such as file not found or connection terminated. You should explicitly handle these errors, and only these.

In all cases except: pass will only leave your program in an unknown state, where it can cause more damage.


回答 11

简而言之,如果引发异常或错误,则说明存在问题。可能不是很不对劲,但是仅仅为了使用goto语句而创建,抛出和捕获错误和异常并不是一个好主意,而且很少这样做。99%的时间,某处出现问题。

需要解决的问题。就像生活中的情况一样,在编程中,如果您只是将问题搁置一旁并尝试忽略它们,那么它们就不会多次自行消失。相反,它们变得更大并成倍增加。为防止问题在您身上蔓延并进一步打击您,您可以1)消除它,然后清理残局,或者2)遏制它,然后清理残局。

只是忽略异常和错误并让它们像那样,是体验内存泄漏,出色的数据库连接,不必要的文件权限锁定等的好方法。

在极少数情况下,问题是如此的微小,琐碎,并且-除了需要try … catch块之外- 自包含的,以至于事后确实没有任何需要清理的地方。在这些情况下,这些最佳做法不一定适用。以我的经验,这通常意味着代码所做的任何事情基本上都是小巧的和可忽略的,而重试尝试或特殊消息之类的东西既不值得其复杂性也不值得其坚持下去。

在我公司,规则是几乎总是在某个陷阱中做某事,如果您什么都不做,那么您必须始终以非常充分的理由发表评论。要做任何事情时,您绝不能通过或留空捕获块。

Simply put, if an exception or error is thrown, something’s wrong. It may not be something very wrong, but creating, throwing, and catching errors and exceptions just for the sake of using goto statements is not a good idea, and it’s rarely done. 99% of the time, there was a problem somewhere.

Problems need to be dealt with. Just like how it is in life, in programming, if you just leave problems alone and try to ignore them, they don’t just go away on their own a lot of times; instead they get bigger and multiply. To prevent a problem from growing on you and striking again further down the road, you either 1) eliminate it and clean up the mess afterwards, or 2) contain it and clean up the mess afterwards.

Just ignoring exceptions and errors and leaving them be like that is a good way to experience memory leaks, outstanding database connections, needless locks on file permissions, etc.

On rare occasions, the problem is so miniscule, trivial, and – aside from needing a try…catch block – self-contained, that there really is just no mess to be cleaned up afterwards. These are the only occasions when this best practice doesn’t necessarily apply. In my experience, this has generally meant that whatever the code is doing is basically petty and forgoable, and something like retry attempts or special messages are worth neither the complexity nor holding the thread up on.

At my company, the rule is to almost always do something in a catch block, and if you don’t do anything, then you must always place a comment with a very good reason why not. You must never pass or leave an empty catch block when there is anything to be done.


回答 12

在我看来,错误是有原因出现的,我的声音很愚蠢,但这就是事实。良好的编程仅在必须处理错误时才会引发错误。另外,正如我前段时间所读到的,“ pass-Statement是一个显示代码的语句将在以后插入”,因此,如果您想拥有一个空的except-statement,可以随意这样做,但是对于一个好的程序,成为一部分。因为你不处理你应该拥有的东西。出现的异常使您有机会更正输入数据或更改数据结构,因此这些异常不会再次发生(但是在大多数情况下(网络异常,常规输入异常),异常表明程序的下一部分将无法正常执行。例如,NetworkException可能指示网络连接断开,并且该程序无法在接下来的程序步骤中发送/接收数据。

但是仅对一个执行块使用pass块是有效的,因为您仍然区分异常类型,因此,如果将所有异常块放在一个中,则它不是空的:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

可以这样重写:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

因此,即使是多个带有通过语句的except-block也会导致代码,其代码可处理特殊类型的异常。

In my opinion errors have a reason to appear, that my sound stupid, but thats the way it is. Good programming only raises errors when you have to handle them. Also, as i read some time ago, “the pass-Statement is a Statement that Shows code will be inserted later”, so if you want to have an empty except-statement feel free to do so, but for a good program there will be a part missing. because you dont handle the things you should have. Appearing exceptions give you the chance to correct input data or to change your data structure so these exceptions dont occur again (but in most cases (Network-exceptions, General input-exceptions) exceptions indicate that the next parts of the program wont execute well. For example a NetworkException can indicate a broken network-connection and the program cant send/recieve data in the next program steps.

But using a pass block for only one execption-block is valid, because you still differenciate beetween the types of exceptions, so if you put all exception-blocks in one, it is not empty:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

can be rewritten that way:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

So even multiple except-blocks with pass-statements can result in code, whose structure handles special types of exceptions.


回答 13

到目前为止提出的所有评论均有效。在可能的情况下,您需要指定要忽略的异常。在可能的情况下,您需要分析导致异常的原因,只忽略您要忽略的内容,而不要忽略其余的内容。如果异常导致应用程序“严重崩溃”,那么就这样吧,因为比起掩盖曾经发生过的问题,了解意外事件在发生时更为重要。

综上所述,不要以任何编程实践为重。真傻 总是有时间和地点进行忽略所有exceptions的阻止。

愚蠢至高无上的另一个例子是goto运算符的用法。当我在学校的时候,我们的教授教我们goto操作员,只是提起您永远不要使用它。不要相信有人告诉您xyz永远不要使用,并且在任何情况下都不会有用。总有。

All comments brought up so far are valid. Where possible you need to specify what exactly exception you want to ignore. Where possible you need to analyze what caused exception, and only ignore what you meant to ignore, and not the rest. If exception causes application to “crash spectacularly”, then be it, because it’s much more important to know the unexpected happened when it happened, than concealing that the problem ever occurred.

With all that said, do not take any programming practice as a paramount. This is stupid. There always is the time and place to do ignore-all-exceptions block.

Another example of idiotic paramount is usage of goto operator. When I was in school, our professor taught us goto operator just to mention that thou shalt not use it, EVER. Don’t believe people telling you that xyz should never be used and there cannot be a scenario when it is useful. There always is.


回答 14

错误处理在编程中非常重要。您确实需要向用户显示出了什么问题。在极少数情况下,您可以忽略这些错误。这是非常糟糕的编程习惯。

​Handling errors is very important in programming. You do need to show the user what went wrong. In very few cases you can ignore the errors. This is it is very bad programming practice.


回答 15

由于尚未提及,因此使用更好的样式contextlib.suppress

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

请注意,在提供的示例中, ,无论是否发生异常程序状态均保持不变。也就是说,somefile.tmp总是不存在。

Since it hasn’t been mentioned yet, it’s better style to use contextlib.suppress:

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

Notice that in the example provided, the program state remains the same, whether or not the exception occurs. That is to say, somefile.tmp always becomes non-existent.