标签归档:try-catch

在try-except块中使用python“ with”语句

问题:在try-except块中使用python“ with”语句

这是将python“ with”语句与try-except块结合使用的正确方法吗?

try:
    with open("file", "r") as f:
        line = f.readline()
except IOError:
    <whatever>

如果是这样,请考虑使用旧的处理方式:

try:
    f = open("file", "r")
    line = f.readline()
except IOError:
    <whatever>
finally:
    f.close()

这里的“ with”语句的主要好处是我们可以摆脱三行代码吗?对于这个用例,这似乎并不吸引我(尽管我知道“ with”语句还有其他用途)。

编辑:以上两个代码块的功能是否相同?

EDIT2:前几个答案大体上谈及使用“ with”的好处,但是这里似乎没有什么好处。我们已经(或者应该已经)明确地调用f.close()多年了。我想一个好处是草率的编码器将从使用“ with”中受益。

Is this the right way to use the python “with” statement in combination with a try-except block?:

try:
    with open("file", "r") as f:
        line = f.readline()
except IOError:
    <whatever>

If it is, then considering the old way of doing things:

try:
    f = open("file", "r")
    line = f.readline()
except IOError:
    <whatever>
finally:
    f.close()

Is the primary benefit of the “with” statement here that we can get rid of three lines of code? It doesn’t seem that compelling to me for this use case (though I understand that the “with” statement has other uses).

EDIT: Is the functionality of the above two blocks of code identical?

EDIT2: The first few answers talk generally about the benefits of using “with”, but those seem of marginal benefit here. We’ve all been (or should have been) explicitly calling f.close() for years. I suppose one benefit is that sloppy coders will benefit from using “with”.


回答 0

  1. 您提供的两个代码块 相等
  2. 您描述为旧的工作方式的代码有一个严重的错误:如果打开文件失败,您将在finally子句中得到第二个异常, 因为f它没有绑定。

等效的旧样式代码为:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

如您所见,该with语句可以减少出错的可能性。在较新版本的Python(2.7,3.1)中,您还可以在一个with语句中组合多个表达式。例如:

with open("input", "r") as inp, open("output", "w") as out:
    out.write(inp.read())

除此之外,我个人认为尽早发现任何异常是一个坏习惯。这不是exceptions的目的。如果可能失败的IO功能是更复杂的操作的一部分,则在大多数情况下,IOError应该中止整个操作,因此应从外部进行处理。使用with语句,您可以消除try...finally内部所有这些语句。

  1. The two code blocks you gave are not equivalent
  2. The code you described as old way of doing things has a serious bug: in case opening the file fails you will get a second exception in the finally clause because f is not bound.

The equivalent old style code would be:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

As you can see, the with statement can make things less error prone. In newer versions of Python (2.7, 3.1), you can also combine multiple expressions in one with statement. For example:

with open("input", "r") as inp, open("output", "w") as out:
    out.write(inp.read())

Besides that, I personally regard it as bad habit to catch any exception as early as possible. This is not the purpose of exceptions. If the IO function that can fail is part of a more complicated operation, in most cases the IOError should abort the whole operation and so be handled at an outer level. Using with statements, you can get rid of all these try...finally statements at inner levels.


回答 1

如果finally块的内容由打开的文件对象的属性决定,那么为什么文件对象的实现者不应该是编写finally块的人呢?这就是with语句的好处,不仅仅是在此特定实例中为您节省三行代码。

是的,你结合的方式with,并try-except为几乎做到这一点的唯一方法,因为内造成特殊错误open本身不能内被捕获的语句with块。

If the contents of the finally block are determined by the properties of the file object being opened, why shouldn’t the implementer of the file object be the one to write the finally block? That’s the benefit of the with statement, much more than saving you three lines of code in this particular instance.

And yes, the way you’ve combined with and try-except is pretty much the only way to do it, as exceptional errors caused within the open statement itself can’t be caught within the with block.


回答 2

我认为您对“ with”语句的理解是错误的,因为它只会减少行数。它实际上进行初始化并处理拆除。

在您的情况下,“ with”确实

  • 打开一个文件,
  • 处理其内容,以及
  • 确保关闭它。

这是用于理解“ with”语句的链接:http : //effbot.org/zone/python-with-statement.htm

编辑:是的,您对“ with”的使用是正确的,并且两个代码块的功能相同。关于为什么要使用“ with”的问题?这是因为您从中受益。就像您提到的意外丢失f.close()一样。

I think you got it wrong about “with” statement that it only reduces lines. It actually does initialization and handle teardown.

In your case “with” does

  • open a file,
  • process its contents, and
  • make sure to close it.

Here is link for understanding “with” statement : http://effbot.org/zone/python-with-statement.htm

Edit: Yes your usage of “with” is correct and functionality of both blocks of code is identical. Question about why to use “with” ? it’s because of benefits you get with it. like you mentioned about accidentally missing f.close().


回答 3

以下代码的更多Pythonic方式是:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

try:
    f = open("file", "r")
except IOError:
    <whatever>
else:
    f.close()

The more Pythonic way for the following codes is:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

try:
    f = open("file", "r")
except IOError:
    <whatever>
else:
    f.close()

无需尝试即可在Python中捕获键盘中断

问题:无需尝试即可在Python中捕获键盘中断

Python中是否有某种方法可以捕获KeyboardInterrupt事件而不将所有代码放入tryexcept语句中?

如果用户按下Ctrl+,我想干净无痕地退出C

Is there some way in Python to capture KeyboardInterrupt event without putting all the code inside a tryexcept statement?

I want to cleanly exit without trace if user presses Ctrl+C.


回答 0

是的,您可以使用模块signal安装中断处理程序,并使用threading.Event永远等待:

import signal
import sys
import time
import threading

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()

Yes, you can install an interrupt handler using the module signal, and wait forever using a threading.Event:

import signal
import sys
import time
import threading

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()

回答 1

如果您只想不显示回溯,则使代码如下所示:

## all your app logic here
def main():
   ## whatever your app does.


if __name__ == "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(是的,我知道这并不能直接回答问题,但是还不清楚为什么需要try / except块会令人反感-也许这会使OP变得不那么烦人了)

If all you want is to not show the traceback, make your code like this:

## all your app logic here
def main():
   ## whatever your app does.


if __name__ == "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(Yes, I know that this doesn’t directly answer the question, but it’s not really clear why needing a try/except block is objectionable — maybe this makes it less annoying to the OP)


回答 2

设置自己的信号处理程序的另一种方法是使用上下文管理器来捕获异常并忽略它:

>>> class CleanExit(object):
...     def __enter__(self):
...             return self
...     def __exit__(self, exc_type, exc_value, exc_tb):
...             if exc_type is KeyboardInterrupt:
...                     return True
...             return exc_type is None
... 
>>> with CleanExit():
...     input()    #just to test it
... 
>>>

这将删除tryexcept块,同时保留一些明确的说明。

这还允许您仅在代码的某些部分中忽略中断,而不必每次都设置和重置信号处理程序。

An alternative to setting your own signal handler is to use a context-manager to catch the exception and ignore it:

>>> class CleanExit(object):
...     def __enter__(self):
...             return self
...     def __exit__(self, exc_type, exc_value, exc_tb):
...             if exc_type is KeyboardInterrupt:
...                     return True
...             return exc_type is None
... 
>>> with CleanExit():
...     input()    #just to test it
... 
>>>

This removes the tryexcept block while preserving some explicit mention of what is going on.

This also allows you to ignore the interrupt only in some portions of your code without having to set and reset again the signal handlers everytime.


回答 3

我知道这是一个古老的问题,但是我首先来到这里,然后发现了该atexit模块。我还不知道它的跨平台跟踪记录或完整的警告说明,但是到目前为止,这正是我KeyboardInterrupt在Linux上尝试进行后期清理时一直在寻找的东西。只是想以另一种方式解决问题。

我想在Fabric操作的上下文中进行退出后清理,因此将所有内容都包装在try/ except中对我来说也不是一种选择。我觉得atexit这种情况可能非常适合,因为您的代码不在控制流的最高级别。

atexit 具有非常强大的功能并且易于使用,例如:

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

您还可以将其用作装饰器(从2.6开始;该示例来自docs):

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

如果您只想使其特定KeyboardInterrupt,那么另一个人对此问题的答案可能会更好。

但是请注意,该atexit模块只有约70行代码,并且创建类似版本以不同方式对待异常(例如将异常作为参数传递给回调函数)并不难。(这样做的局限性是atexit需要修改后的版本:目前,我无法为exit-callback-functions知道异常的方法;atexit处理程序捕获异常,调用回调,然后重新引发该exceptions。但是您可以采取不同的方法。)

有关更多信息,请参见:

I know this is an old question but I came here first and then discovered the atexit module. I do not know about its cross-platform track record or a full list of caveats yet, but so far it is exactly what I was looking for in trying to handle post-KeyboardInterrupt cleanup on Linux. Just wanted to throw in another way of approaching the problem.

I want to do post-exit clean-up in the context of Fabric operations, so wrapping everything in try/except wasn’t an option for me either. I feel like atexit may be a good fit in such a situation, where your code is not at the top level of control flow.

atexit is very capable and readable out of the box, for example:

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

You can also use it as a decorator (as of 2.6; this example is from the docs):

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

If you wanted to make it specific to KeyboardInterrupt only, another person’s answer to this question is probably better.

But note that the atexit module is only ~70 lines of code and it would not be hard to create a similar version that treats exceptions differently, for example passing the exceptions as arguments to the callback functions. (The limitation of atexit that would warrant a modified version: currently I can’t conceive of a way for the exit-callback-functions to know about the exceptions; the atexit handler catches the exception, calls your callback(s), then re-raises that exception. But you could do this differently.)

For more info see:


回答 4

您可以通过替换来防止打印堆栈跟踪KeyboardInterrupt,而无需try: ... except KeyboardInterrupt: pass(最明显,最恰当的“最佳”解决方案,但您已经知道并要求其他东西)sys.excepthook。就像是

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)

You can prevent printing a stack trace for KeyboardInterrupt, without try: ... except KeyboardInterrupt: pass (the most obvious and propably “best” solution, but you already know it and asked for something else) by replacing sys.excepthook. Something like

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)

回答 5

我尝试了每个人提出的建议解决方案,但我必须自己临时编写代码才能真正起作用。以下是我的即兴代码:

import signal
import sys
import time

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    print(signal) # Value is 2 for CTRL + C
    print(frame) # Where your execution of program is at moment - the Line Number
    sys.exit(0)

#Assign Handler Function
signal.signal(signal.SIGINT, signal_handler)

# Simple Time Loop of 5 Seconds
secondsCount = 5
print('Press Ctrl+C in next '+str(secondsCount))
timeLoopRun = True 
while timeLoopRun:  
    time.sleep(1)
    if secondsCount < 1:
        timeLoopRun = False
    print('Closing in '+ str(secondsCount)+ ' seconds')
    secondsCount = secondsCount - 1

I tried the suggested solutions by everyone, but I had to improvise code myself to actually make it work. Following is my improvised code:

import signal
import sys
import time

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    print(signal) # Value is 2 for CTRL + C
    print(frame) # Where your execution of program is at moment - the Line Number
    sys.exit(0)

#Assign Handler Function
signal.signal(signal.SIGINT, signal_handler)

# Simple Time Loop of 5 Seconds
secondsCount = 5
print('Press Ctrl+C in next '+str(secondsCount))
timeLoopRun = True 
while timeLoopRun:  
    time.sleep(1)
    if secondsCount < 1:
        timeLoopRun = False
    print('Closing in '+ str(secondsCount)+ ' seconds')
    secondsCount = secondsCount - 1

回答 6

如果有人正在寻找快速的最小解决方案,

import signal

# The code which crashes program on interruption

signal.signal(signal.SIGINT, call_this_function_if_interrupted)

# The code skipped if interrupted

If someone is in search for a quick minimal solution,

import signal

# The code which crashes program on interruption

signal.signal(signal.SIGINT, call_this_function_if_interrupted)

# The code skipped if interrupted

更好地“尝试”某些东西并捕获异常或测试是否有可能首先避免异常?

问题:更好地“尝试”某些东西并捕获异常或测试是否有可能首先避免异常?

我应该测试if某种东西是有效的还是只是try为了做它并捕获异常?

  • 有没有可靠的文档说首选方法?
  • 还有一种方法更pythonic吗?

例如,我应该:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

要么:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

一些想法…
PEP 20说:

错误绝不能默默传递。
除非明确地保持沉默。

应该使用a try而不是an if解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?


不是指只能以一种方式做事的情况;例如:

try:
    import foo
except ImportError:
    import baz

Should I test if something is valid or just try to do it and catch the exception?

  • Is there any solid documentation saying that one way is preferred?
  • Is one way more pythonic?

For example, should I:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

Or:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

Some thoughts…
PEP 20 says:

Errors should never pass silently.
Unless explicitly silenced.

Should using a try instead of an if be interpreted as an error passing silently? And if so, are you explicitly silencing it by using it in this way, therefore making it OK?


I’m not referring to situations where you can only do things 1 way; for example:

try:
    import foo
except ImportError:
    import baz

回答 0

你应该更喜欢try/exceptif/else如果结果

  • 加快速度(例如,通过防止额外的查询)
  • 更清晰的代码(行数更少/更易于阅读)

通常,它们并存。


加速

如果尝试通过以下方式在长列表中查找元素:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

index可能在列表中并且通常不引发IndexError 时,尝试除外是最好的选择。这样,您就可以避免进行额外的查询if index < len(my_list)

Python鼓励使用异常,可以使用Dive Into Python中的短语来处理异常。您的示例不仅(优美地)处理异常,而不是让其静默通过,而且仅在未找到索引的特殊情况下才发生异常(因此,单词异常!)。


清洁代码

Python的官方文档中提到了EAFP比获得许可更容易获得宽恕Rob Knight指出捕获错误而不是避免错误可以使代码简洁,更易于阅读。他的示例如下所示:

更差(LBYL“跳前先看”)

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

更好(EAFP:寻求宽恕比获得许可更容易)

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

You should prefer try/except over if/else if that results in

  • speed-ups (for example by preventing extra lookups)
  • cleaner code (fewer lines/easier to read)

Often, these go hand-in-hand.


speed-ups

In the case of trying to find an element in a long list by:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

the try, except is the best option when the index is probably in the list and the IndexError is usually not raised. This way you avoid the need for an extra lookup by if index < len(my_list).

Python encourages the use of exceptions, which you handle is a phrase from Dive Into Python. Your example not only handles the exception (gracefully), rather than letting it silently pass, also the exception occurs only in the exceptional case of index not being found (hence the word exception!).


cleaner code

The official Python Documentation mentions EAFP: Easier to ask for forgiveness than permission and Rob Knight notes that catching errors rather than avoiding them, can result in cleaner, easier to read code. His example says it like this:

Worse (LBYL ‘look before you leap’):

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

Better (EAFP: Easier to ask for forgiveness than permission):

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

回答 1

在这种情况下,您应该完全使用其他方法:

x = myDict.get("ABC", "NO_ABC")

不过,通常来说:如果您希望测试经常失败,请使用if。如果测试相对于尝试操作并失败则捕获异常而言代价高昂,请使用try。如果以上条件均不适用,则更容易阅读。

In this particular case, you should use something else entirely:

x = myDict.get("ABC", "NO_ABC")

In general, though: If you expect the test to fail frequently, use if. If the test is expensive relative to just trying the operation and catching the exception if it fails, use try. If neither one of these conditions applies, go with whatever reads easier.


回答 2

使用tryexcept直接,而不是内侧if后卫应该始终是否有竞争条件的可能性来完成。例如,如果要确保目录存在,请不要执行以下操作:

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

如果另一个线程或进程在isdir和之间创建目录,则将mkdir退出。相反,请执行以下操作:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

仅当无法创建’foo’目录时,该命令才会退出。

Using try and except directly rather than inside an if guard should always be done if there is any possibility of a race condition. For example, if you want to ensure that a directory exists, do not do this:

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

If another thread or process creates the directory between isdir and mkdir, you’ll exit. Instead, do this:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

That will only exit if the ‘foo’ directory can’t be created.


回答 3

如果在进行某些操作之前先检查一下是否会失败,那么您可能应该赞成这样做。毕竟,构造异常(包括相关的回溯)需要花费时间。

异常应用于:

  1. 出乎意料的事情,或者…
  2. 您需要跳到不只一个逻辑层次的事情(例如a break不能使您走得太远),或者…
  3. 您不确切知道该如何提前处理异常的事情,或者…
  4. 提前检查故障的成本很高(相对于尝试操作而言)

请注意,通常,真正的答案是“都不是”-例如,在第一个示例中,您真正应该做的只是.get()提供默认值:

x = myDict.get('ABC', 'NO_ABC')

If it’s trivial to check whether something will fail before you do it, you should probably favor that. After all, constructing exceptions (including their associated tracebacks) takes time.

Exceptions should be used for:

  1. things that are unexpected, or…
  2. things where you need to jump more than one level of logic (e.g. where a break doesn’t get you far enough), or…
  3. things where you don’t know exactly what is going to be handling the exception ahead of time, or…
  4. things where checking ahead of time for failure is expensive (relative to just attempting the operation)

Note that oftentimes, the real answer is “neither” – for instance, in your first example, what you really should do is just use .get() to provide a default:

x = myDict.get('ABC', 'NO_ABC')

回答 4

正如其他职位所提到的,这取决于情况。使用try / except代替预先检查数据的有效性存在一些危险,尤其是在较大的项目中使用时。

  • 在try块中的代码可能有机会在捕获异常之前进行各种破坏-如果您事先使用if语句主动进行检查,则可以避免这种情况。
  • 如果在try块中调用的代码引发了一个常见的异常类型(如TypeError或ValueError),则您实际上可能没有捕获到您期望捕获的相同异常-可能是其他原因导致甚至在进入之前或之后引发相同的异常类可能引发异常的行。

例如,假设您有:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexError没有任何关于尝试获取index_list或my_list元素时是否发生的信息。

As the other posts mention, it depends on the situation. There are a few dangers with using try/except in place of checking the validity of your data in advance, especially when using it on bigger projects.

  • The code in the try block may have a chance to wreak all sorts of havoc before the exception is caught – if you proactively check beforehand with an if statement you can avoid this.
  • If the code called in your try block raises a common exception type, like TypeError or ValueError, you may not actually catch the same exception you were expecting to catch – it may be something else that raise the same exception class before or after even getting to the line where your exception may be raised.

e.g., suppose you had:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

The IndexError says nothing about whether it occurred when trying to get an element of index_list or my_list.


回答 5

应该使用try而不是if来解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?

使用try表示可能会通过错误,这与使其静默通过相反。使用except导致它根本不通过。

try: except:if: else:逻辑更为复杂的情况下,首选使用。简单胜于复杂。复杂胜于复杂;要求宽恕比允许容易。

警告:“错误永远都不能静默传递”,是代码可能引发您所知道的异常,并且您的设计承认存在这种可能性的情况,但您并未以处理异常的方式进行设计。在我看来,明确地消除错误将像passexcept块中那样进行,仅应在了解“不做任何事情”确实是特定情况下的正确错误处理的情况下进行操作。(这是我真正需要使用编写良好的代码进行注释的少数几次。)

但是,在您的特定示例中,都不适合:

x = myDict.get('ABC', 'NO_ABC')

每个人都指出这一点的原因-即使您承认您希望总体上理解并且无法提出更好的例子-是在很多情况下实际上存在等效的避让,而寻找它们是解决问题的第一步。

Should using a try instead of an if be interpreted as an error passing silently? And if so, are you explicitly silencing it by using it in this way, therefore making it OK?

Using try is acknowledging that an error may pass, which is the opposite of having it pass silently. Using except is causing it not to pass at all.

Using try: except: is preferred in cases where if: else: logic is more complicated. Simple is better than complex; complex is better than complicated; and it’s easier to ask for forgiveness than permission.

What “errors should never pass silently” is warning about, is the case where code could raise an exception that you know about, and where your design admits the possibility, but you haven’t designed in a way to deal with the exception. Explicitly silencing an error, in my view, would be doing something like pass in an except block, which should only be done with an understanding that “doing nothing” really is the correct error handling in the particular situation. (This is one of the few times where I feel like a comment in well-written code is probably really needed.)

However, in your particular example, neither is appropriate:

x = myDict.get('ABC', 'NO_ABC')

The reason everyone is pointing this out – even though you acknowledge your desire to understand in general, and inability to come up with a better example – is that equivalent side-steps actually exist in quite a lot of cases, and looking for them is the first step in solving the problem.


回答 6

每当try/except用于控制流时,请问自己:

  1. 是否容易看到该try块何时成功以及何时失败?
  2. 您是否知道该区块内的所有副作用try
  3. 您是否知道该块引发异常的所有情况try
  4. 如果该try块的实现发生更改,您的控制流是否仍将按预期运行?

如果对这些问题中的一个或多个的回答为“否”,则可能会有很多宽容的要求。最有可能来自您未来的自我。


一个例子。我最近在一个更大的项目中看到了如下代码:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

与程序员交谈后,发现预期的控制流程为:

如果x是整数,则y = foo(x)。

如果x是整数列表,则y = bar(x)。

之所以foo可行,是因为进行了数据库查询,如果x为整数,则查询将成功,如果为列表,ProgrammingError则将抛出if x

try/except在这里使用是一个不好的选择:

  1. 异常的名称ProgrammingError不会给出实际的问题(x不是整数),这使得很难看到发生了什么。
  2. ProgrammingError数据库调用,浪费时间内上升。如果事实证明是foo在引发异常之前将某些内容写入数据库或更改了其他系统的状态,那么事情将变得非常可怕。
  3. 尚不清楚是否ProgrammingError仅在x整数列表时才引发。例如,假设foo的数据库查询中有错字。这可能还会引发一个ProgrammingError。结果是,bar(x)x是整数时,现在也称为。这可能会引发神秘异常或产生不可预见的结果。
  4. try/except块为的所有未来实现增加了要求foo。每当我们进行更改时foo,我们现在都必须考虑它如何处理列表,并确保它引发一个错误,ProgrammingError而不是一个AttributeError或根本不引发一个错误。

Whenever you use try/except for control flow, ask yourself:

  1. Is it easy to see when the try block succeeds and when it fails?
  2. Are you aware of all side effects inside the try block?
  3. Are you aware of all cases in which the try block throws the exception?
  4. If the implementation of the try block changes, will your control flow still behave as expected?

If the answer to one or more of these questions is ‘no’, there might be a lot of forgiveness to ask for; most likely from your future self.


An example. I recently saw code in a larger project that looked like this:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

Talking to the programmer it turned that the intended control flow was:

If x is an integer, do y = foo(x).

If x is a list of integers, do y = bar(x).

This worked because foo made a database query and the query would be successful if x was an integer and throw a ProgrammingError if x was a list.

Using try/except is a bad choice here:

  1. The name of the exception, ProgrammingError, does not give away the actual problem (that x is not an integer), which makes it difficult to see what is going on.
  2. The ProgrammingError is raised during a database call, which wastes time. Things would get truly horrible if it turned out that foo writes something to the database before it throws an exception, or alters the state of some other system.
  3. It is unclear if ProgrammingError is only raised when x is a list of integers. Suppose for instance that there is a typo in foo‘s database query. This might also raise a ProgrammingError. The consequence is that bar(x) is now also called when x is an integer. This might raise cryptic exceptions or produce unforeseeable results.
  4. The try/except block adds a requirement to all future implementations of foo. Whenever we change foo, we must now think about how it handles lists and make sure that it throws a ProgrammingError and not, say, an AttributeError or no error at all.

回答 7

对于一般含义,您可以考虑阅读Python中的成语和反成语:异常

在您的特定情况下,如其他人所述,您应该使用dict.get()

get(key [,默认])

如果key在字典中,则返回key的值,否则返回默认值。如果未提供default,则默认为None,因此此方法永远不会引发KeyError。

For a general meaning, you may consider reading Idioms and Anti-Idioms in Python: Exceptions.

In your particular case, as others stated, you should use dict.get():

get(key[, default])

Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.


为什么“ 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.


在Python中使用try-except-else是否是一种好习惯?

问题:在Python中使用try-except-else是否是一种好习惯?

在Python中,我不时看到该块:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

try-except-else存在的原因是什么?

我不喜欢这种编程,因为它使用异常来执行流控制。但是,如果它包含在语言中,则一定有充分的理由,不是吗?

据我了解,异常不是错误,并且仅应将其用于特殊情况(例如,我尝试将文件写入磁盘,并且没有更多空间,或者我没有权限),而不是流控制。

通常,我将异常处理为:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

或者,如果发生异常,我真的不想返回任何东西,那么:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

From time to time in Python, I see the block:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

What is the reason for the try-except-else to exist?

I do not like that kind of programming, as it is using exceptions to perform flow control. However, if it is included in the language, there must be a good reason for it, isn’t it?

It is my understanding that exceptions are not errors, and that they should only be used for exceptional conditions (e.g. I try to write a file into disk and there is no more space, or maybe I do not have permission), and not for flow control.

Normally I handle exceptions as:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

Or if I really do not want to return anything if an exception happens, then:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

回答 0

“我不知道它是否出于无知,但我不喜欢这种编程,因为它使用异常来执行流控制。”

在Python世界中,使用异常进行流控制是常见且正常的。

甚至Python核心开发人员也将异常用于流控制,并且该样式已在语言中大量使用(即,迭代器协议使用StopIteration发出信号以终止循环)。

此外,try-except样式用于防止某些“跨越式”构造固有的竞争条件。例如,测试os.path.exists会导致信息在您使用时已过时。同样,Queue.full返回的信息可能已过时。在这种情况下,try-except-else样式将产生更可靠的代码。

“据我了解,异常不是错误,它们仅应用于特殊情况”

在其他一些语言中,该规则反映了图书馆所反映的文化规范。该“规则”还部分基于这些语言的性能考虑。

Python的文化规范有些不同。在许多情况下,必须对控制流使用exceptions。另外,在Python中使用异常不会像在某些编译语言中那样降低周围的代码和调用代码的速度(即CPython已经在每一步实现了用于异常检查的代码,而不管您是否实际使用异常)。

换句话说,您理解“exceptions是为了exceptions”是一条在其他语言中有意义的规则,但不适用于Python。

“但是,如果它本身包含在语言中,那一定有充分的理由,不是吗?”

除了帮助避免竞争条件外,异常对于在循环外拉出错误处理也非常有用。这是解释语言中的必要优化,这些语言通常不会具有自动循环不变的代码运动

另外,在通常情况下,异常可以大大简化代码,在正常情况下,处理问题的能力与问题发生的地方相距甚远。例如,通常有用于业务逻辑的顶级用户界面代码调用代码,而后者又调用低级例程。低级例程中出现的情况(例如数据库访问中唯一键的重复记录)只能以顶级代码处理(例如,要求用户提供与现有键不冲突的新键)。对此类控制流使用异常可以使中级例程完全忽略该问题,并将其与流控制的这一方面很好地分离。

这里有一篇关于异常必不可少的不错的博客文章

另外,请参见此堆栈溢出答案:异常真的是异常错误吗?

“ try-except-else存在的原因是什么?”

其他条款本身很有趣。它在没有exceptions的情况下运行,但是在最终条款之前。这是其主要目的。

如果没有else子句,那么在最终确定之前运行其他代码的唯一选择就是将代码添加到try子句的笨拙做法。这很笨拙,因为它冒着在代码中引发异常的危险,而这些异常本来不会受到try块的保护。

在完成之前运行其他不受保护的代码的用例很少出现。因此,不要期望在已发布的代码中看到很多示例。这有点罕见。

else子句的另一个用例是执行在没有异常发生时必须发生的动作以及在处理异常时不发生的动作。例如:

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

另一个示例发生在单元测试赛跑者中:

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

最后,在尝试块中最常用的else子句是为了美化一些(在相同的缩进级别上对齐exceptions结果和非exceptions结果)。此用法始终是可选的,并非严格必要。

“I do not know if it is out of ignorance, but I do not like that kind of programming, as it is using exceptions to perform flow control.”

In the Python world, using exceptions for flow control is common and normal.

Even the Python core developers use exceptions for flow-control and that style is heavily baked into the language (i.e. the iterator protocol uses StopIteration to signal loop termination).

In addition, the try-except-style is used to prevent the race-conditions inherent in some of the “look-before-you-leap” constructs. For example, testing os.path.exists results in information that may be out-of-date by the time you use it. Likewise, Queue.full returns information that may be stale. The try-except-else style will produce more reliable code in these cases.

“It my understanding that exceptions are not errors, they should only be used for exceptional conditions”

In some other languages, that rule reflects their cultural norms as reflected in their libraries. The “rule” is also based in-part on performance considerations for those languages.

The Python cultural norm is somewhat different. In many cases, you must use exceptions for control-flow. Also, the use of exceptions in Python does not slow the surrounding code and calling code as it does in some compiled languages (i.e. CPython already implements code for exception checking at every step, regardless of whether you actually use exceptions or not).

In other words, your understanding that “exceptions are for the exceptional” is a rule that makes sense in some other languages, but not for Python.

“However, if it is included in the language itself, there must be a good reason for it, isn’t it?”

Besides helping to avoid race-conditions, exceptions are also very useful for pulling error-handling outside loops. This is a necessary optimization in interpreted languages which do not tend to have automatic loop invariant code motion.

Also, exceptions can simplify code quite a bit in common situations where the ability to handle an issue is far removed from where the issue arose. For example, it is common to have top level user-interface code calling code for business logic which in turn calls low-level routines. Situations arising in the low-level routines (such as duplicate records for unique keys in database accesses) can only be handled in top-level code (such as asking the user for a new key that doesn’t conflict with existing keys). The use of exceptions for this kind of control-flow allows the mid-level routines to completely ignore the issue and be nicely decoupled from that aspect of flow-control.

There is a nice blog post on the indispensibility of exceptions here.

Also, see this Stack Overflow answer: Are exceptions really for exceptional errors?

“What is the reason for the try-except-else to exist?”

The else-clause itself is interesting. It runs when there is no exception but before the finally-clause. That is its primary purpose.

Without the else-clause, the only option to run additional code before finalization would be the clumsy practice of adding the code to the try-clause. That is clumsy because it risks raising exceptions in code that wasn’t intended to be protected by the try-block.

The use-case of running additional unprotected code prior to finalization doesn’t arise very often. So, don’t expect to see many examples in published code. It is somewhat rare.

Another use-case for the else-clause is to perform actions that must occur when no exception occurs and that do not occur when exceptions are handled. For example:

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

Another example occurs in unittest runners:

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

Lastly, the most common use of an else-clause in a try-block is for a bit of beautification (aligning the exceptional outcomes and non-exceptional outcomes at the same level of indentation). This use is always optional and isn’t strictly necessary.


回答 1

try-except-else存在的原因是什么?

一个try块可以处理预期的错误。该except块应该只捕获您准备处理的异常。如果您处理了意外错误,则您的代码可能会做错事情并隐藏错误。

else如果没有错误,将执行一个子句,通过不执行该代码try块中的代码,可以避免捕获意外错误。同样,捕获意外错误可能会隐藏错误。

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

“ try,except”套件有两个可选子句,elsefinally。所以实际上是try-except-else-finally

else仅在try块中没有异常的情况下才会评估。它使我们能够简化下面更复杂的代码:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

因此,如果将a else与替代方案(可能会产生错误)进行比较,我们会发现它减少了代码行,并且我们可以拥有更具可读性,可维护性和更少错误的代码库。

finally

finally 即使使用return语句对另一行进行评估,它也将执行。

用伪代码分解

这可能有助于以尽可能小的形式展示所有功能并带有注释来分解此内容。假定此语法在语法上正确(但除非定义名称,否则不可运行)伪代码在函数中。

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

的确,我们可以将代码包含在else块中的代码中try,如果没有异常,它将在其中运行,但是如果该代码本身引发了我们正在捕获的异常,该怎么办?将其留在try块中将隐藏该错误。

我们希望最小化try块中的代码行,以避免捕获我们未曾想到的异常,其原理是,如果我们的代码失败,我们希望它大声失败。这是最佳做法

据我了解,异常不是错误

在Python中,大多数exceptions都是错误。

我们可以使用pydoc查看异常层次结构。例如,在Python 2中:

$ python -m pydoc exceptions

或Python 3:

$ python -m pydoc builtins

将给我们层次结构。我们可以看到大多数Exception错误都是错误的,尽管Python使用其中的一些错误来结束for循环(StopIteration)。这是Python 3的层次结构:

BaseException
    Exception
        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
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

有评论者问:

假设您有一个可对外部API进行ping的方法,并且想在API包装器之外的类上处理异常,那么您是否只是从方法中的except子句中返回e,其中e是异常对象?

不,您不返回该异常,只需将其重新引发raise以保留堆栈跟踪即可。

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

或者,在Python 3中,您可以引发新的异常并通过异常链接保留回溯:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

我在这里详细回答

What is the reason for the try-except-else to exist?

A try block allows you to handle an expected error. The except block should only catch exceptions you are prepared to handle. If you handle an unexpected error, your code may do the wrong thing and hide bugs.

An else clause will execute if there were no errors, and by not executing that code in the try block, you avoid catching an unexpected error. Again, catching an unexpected error can hide bugs.

Example

For example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

The “try, except” suite has two optional clauses, else and finally. So it’s actually try-except-else-finally.

else will evaluate only if there is no exception from the try block. It allows us to simplify the more complicated code below:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

so if we compare an else to the alternative (which might create bugs) we see that it reduces the lines of code and we can have a more readable, maintainable, and less buggy code-base.

finally

finally will execute no matter what, even if another line is being evaluated with a return statement.

Broken down with pseudo-code

It might help to break this down, in the smallest possible form that demonstrates all features, with comments. Assume this syntactically correct (but not runnable unless the names are defined) pseudo-code is in a function.

For example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

It is true that we could include the code in the else block in the try block instead, where it would run if there were no exceptions, but what if that code itself raises an exception of the kind we’re catching? Leaving it in the try block would hide that bug.

We want to minimize lines of code in the try block to avoid catching exceptions we did not expect, under the principle that if our code fails, we want it to fail loudly. This is a best practice.

It is my understanding that exceptions are not errors

In Python, most exceptions are errors.

We can view the exception hierarchy by using pydoc. For example, in Python 2:

$ python -m pydoc exceptions

or Python 3:

$ python -m pydoc builtins

Will give us the hierarchy. We can see that most kinds of Exception are errors, although Python uses some of them for things like ending for loops (StopIteration). This is Python 3’s hierarchy:

BaseException
    Exception
        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
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

A commenter asked:

Say you have a method which pings an external API and you want to handle the exception at a class outside the API wrapper, do you simply return e from the method under the except clause where e is the exception object?

No, you don’t return the exception, just reraise it with a bare raise to preserve the stacktrace.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

Or, in Python 3, you can raise a new exception and preserve the backtrace with exception chaining:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

I elaborate in my answer here.


回答 2

Python不赞成将异常仅用于特殊情况的想法,实际上,习惯用法是“要求宽恕,而不是允许”。这意味着将异常作为流程控制的常规部分是完全可以接受的,并且实际上是受到鼓励的。

通常,这是一件好事,因为以这种方式工作有助于避免某些问题(显而易见的示例是,通常避免出现竞争条件),并且它倾向于使代码更具可读性。

假设您遇到这样一种情况,您需要处理一些用户输入,但是已经处理了默认输入。该try: ... except: ... else: ...结构使代码易于阅读:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

与其他语言可能的工作方式进行比较:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

注意优点。无需检查该值是否有效并单独对其进行分析,只需完成一次即可。代码也遵循更合理的顺序,首先是主代码路径,然后是“如果不起作用,请执行此操作”。

该示例自然有点虚构,但它显示了这种结构的情况。

Python doesn’t subscribe to the idea that exceptions should only be used for exceptional cases, in fact the idiom is ‘ask for forgiveness, not permission’. This means that using exceptions as a routine part of your flow control is perfectly acceptable, and in fact, encouraged.

This is generally a good thing, as working this way helps avoid some issues (as an obvious example, race conditions are often avoided), and it tends to make code a little more readable.

Imagine you have a situation where you take some user input which needs to be processed, but have a default which is already processed. The try: ... except: ... else: ... structure makes for very readable code:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

Compare to how it might work in other languages:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

Note the advantages. There is no need to check the value is valid and parse it separately, they are done once. The code also follows a more logical progression, the main code path is first, followed by ‘if it doesn’t work, do this’.

The example is naturally a little contrived, but it shows there are cases for this structure.


回答 3

在python中使用try-except-else是否是一种好习惯?

答案是它取决于上下文。如果您这样做:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

它表明您不太了解Python。此功能封装在dict.get方法中:

item = d.get('item', 'default')

try/ except块是写什么都可以有效地在一行用原子方法执行的视觉上更多混乱和冗长的方式。在其他情况下,这是正确的。

但是,这并不意味着我们应该避免所有异常处理。在某些情况下,最好避免比赛条件。不要检查文件是否存在,只需尝试将其打开,然后捕获相应的IOError。为了简单起见,请尝试将其封装或分解为适当的名称。

阅读PythonZen,了解其中存在一些紧绷的原则,并且要警惕过于依赖其中任何一条语句的教条。

Is it a good practice to use try-except-else in python?

The answer to this is that it is context dependent. If you do this:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

It demonstrates that you don’t know Python very well. This functionality is encapsulated in the dict.get method:

item = d.get('item', 'default')

The try/except block is a much more visually cluttered and verbose way of writing what can be efficiently executing in a single line with an atomic method. There are other cases where this is true.

However, that does not mean that we should avoid all exception handling. In some cases it is preferred to avoid race conditions. Don’t check if a file exists, just attempt to open it, and catch the appropriate IOError. For the sake of simplicity and readability, try to encapsulate this or factor it out as apropos.

Read the Zen of Python, understanding that there are principles that are in tension, and be wary of dogma that relies too heavily on any one of the statements in it.


回答 4

请参见以下示例,该示例说明了有关try-except-else-finally的所有信息:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

实施它并获得:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

See the following example which illustrate everything about try-except-else-finally:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

Implement it and come by:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

回答 5

您应谨慎使用finally块,因为它与try中使用else块的功能不同,除了。无论try的结果如何,都将运行finally块。

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

正如所有人都指出的那样,使用else块会使您的代码更具可读性,并且仅在未引发异常时运行

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

You should be careful about using the finally block, as it is not the same thing as using an else block in the try, except. The finally block will be run regardless of the outcome of the try except.

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

As everyone has noted using the else block causes your code to be more readable, and only runs when an exception is not thrown

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

回答 6

每当您看到以下内容时:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

甚至这个:

try:
    return 1 / x
except ZeroDivisionError:
    return None

考虑一下这个:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x

Whenever you see this:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

Or even this:

try:
    return 1 / x
except ZeroDivisionError:
    return None

Consider this instead:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x

回答 7

只是因为没有人发表过这一意见,我会说

避免使用else条款,因为大多数人都不熟悉这些条款try/excepts

与关键字tryexcept和和不同finally,该else子句的含义不言而喻。它的可读性较差。因为它不经常使用,所以它将导致阅读您的代码的人想要仔细检查文档,以确保他们了解正在发生的事情。

(我之所以写此答案,恰恰是因为我try/except/else在代码库中找到了a ,它导致了wtf时刻并迫使我进行了谷歌搜索)。

因此,无论我在哪里看到类似OP示例的代码:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

我宁愿重构为

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# do some more processing in non-exception case  <2>
return something
  • <1>明确的回报清楚地表明,在exceptions情况下,我们已经完成工作

  • <2>作为一个很好的次要副作用,该else块中的代码以前经过了一个级别的确定。

Just because no-one else has posted this opinion, I would say

avoid else clauses in try/excepts because they’re unfamiliar to most people

Unlike the keywords try, except, and finally, the meaning of the else clause isn’t self-evident; it’s less readable. Because it’s not used very often, it’ll cause people that read your code to want to double-check the docs to be sure they understand what’s going on.

(I’m writing this answer precisely because I found a try/except/else in my codebase and it caused a wtf moment and forced me to do some googling).

So, wherever I see code like the OP example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

I would prefer to refactor to

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# do some more processing in non-exception case  <2>
return something
  • <1> explicit return, clearly shows that, in the exception case, we are finished working

  • <2> as a nice minor side-effect, the code that used to be in the else block is dedented by one level.


回答 8

这是我关于如何理解Python中try-except-else-finally块的简单代码段:

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

让我们尝试div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

让我们尝试div 1/0

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

This is my simple snippet on howto understand try-except-else-finally block in Python:

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

Let’s try div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

Let’s try div 1/0

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

回答 9

OP,您是正确的。 在Python中try / except之后的else很难看。它导致另一个不需要的流控制对象:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

完全清楚的等效项是:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

这比else子句清楚得多。try / except之后的else不经常编写,因此花一点时间弄清楚其含义是什么。

仅仅因为您可以做某事,并不意味着您应该做某事。

语言已经添加了许多功能,因为有人认为它可能派上用场。麻烦的是,功能越多,事物的清晰度和显而易见性就越差,这是因为人们通常不使用那些钟声和口哨声。

这里只有我的5美分。我必须走到后面,清理掉大学一年级开发人员写的很多代码,这些开发人员认为他们很聪明,并希望以超级严格,超级高效的方式编写代码,这只会使事情变得一团糟以尝试稍后阅读/修改。我每天对可读性进行投票,而星期日则两次。

OP, YOU ARE CORRECT. The else after try/except in Python is ugly. it leads to another flow-control object where none is needed:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

A totally clear equivalent is:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

This is far clearer than an else clause. The else after try/except is not frequently written, so it takes a moment to figure what the implications are.

Just because you CAN do a thing, doesn’t mean you SHOULD do a thing.

Lots of features have been added to languages because someone thought it might come in handy. Trouble is, the more features, the less clear and obvious things are because people don’t usually use those bells and whistles.

Just my 5 cents here. I have to come along behind and clean up a lot of code written by 1st-year out of college developers who think they’re smart and want to write code in some uber-tight, uber-efficient way when that just makes it a mess to try and read / modify later. I vote for readability every day and twice on Sundays.