标签归档:exception-handling

Python“提高”用法

问题:Python“提高”用法

raiseraise fromPython 和有什么不一样?

try:
    raise ValueError
except Exception as e:
    raise IndexError

产生

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

产生

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

What’s the difference between raise and raise from in Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

which yields

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

and

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

which yields

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

回答 0

区别在于,当您使用时from,会设置__cause__属性,并且消息会指出异常是由引起的。如果您省略,from__cause__设置no ,但是也可以设置该__context__属性,然后回溯显示上下文,就像处理其他事件时一样

__context__如果raise在异常处理程序中使用过,则设置发生的情况;如果您raise在其他任何地方使用过,则也不会__context__设置。

如果__cause__设置了a,那么__suppress_context__ = True还会在异常上设置一个标志;当__suppress_context__设置为时,在打印回溯时True__context__忽略。

从异常处理程序,你养的时候希望显示上下文(不想处理另一个异常发生时,然后用消息)raise ... from None来设置__suppress_context__True

换句话说,Python 在异常上设置了一个上下文,因此您可以自省引发异常的位置,让您查看是否用它替换了另一个异常。您还可以将原因添加到异常中,使回溯明确地显示其他异常(使用不同的措辞),并且上下文将被忽略(但在调试时仍可以自省)。使用raise ... from None使您可以禁止打印上下文。

请参阅raise语句documenation

from子句用于异常链接:如果给定,则第二个表达式必须是另一个异常类或实例,然后将其作为__cause__属性(可写)附加到引发的异常上。如果未处理引发的异常,则将打印两个异常:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

如果在异常处理程序或finally子句中引发异常,则类似的机制会隐式工作:然后,将先前的异常附加为新异常的__context__属性:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

另请参阅内置异常文档,以获取有关上下文的详细信息,并提供附加到异常的原因信息。

The difference is that when you use from, the __cause__ attribute is set and the message states that the exception was directly caused by. If you omit the from then no __cause__ is set, but the __context__ attribute may be set as well, and the traceback then shows the context as during handling something else happened.

Setting the __context__ happens if you used raise in an exception handler; if you used raise anywhere else no __context__ is set either.

If a __cause__ is set, a __suppress_context__ = True flag is also set on the exception; when __suppress_context__ is set to True, the __context__ is ignored when printing a traceback.

When raising from a exception handler where you don’t want to show the context (don’t want a during handling another exception happened message), then use raise ... from None to set __suppress_context__ to True.

In other words, Python sets a context on exceptions so you can introspect where an exception was raised, letting you see if another exception was replaced by it. You can also add a cause to an exception, making the traceback explicit about the other exception (use different wording), and the context is ignored (but can still be introspected when debugging). Using raise ... from None lets you suppress the context being printed.

See the raise statement documenation:

The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable). If the raised exception is not handled, both exceptions will be printed:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

A similar mechanism works implicitly if an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Also see the Built-in Exceptions documentation for details on the context and cause information attached to exceptions.


Python:一次尝试多项

问题:Python:一次尝试多项

在Python中,except一个try语句是否可以有多个语句?如 :

try:
 #something1
 #something2
except ExceptionType1:
 #return xyz
except ExceptionType2:
 #return abc

In Python, is it possible to have multiple except statements for one try statement? Such as :

try:
 #something1
 #something2
except ExceptionType1:
 #return xyz
except ExceptionType2:
 #return abc

回答 0

是的,有可能。

try:
   ...
except FirstException:
   handle_first_one()

except SecondException:
   handle_second_one()

except (ThirdException, FourthException, FifthException) as e:
   handle_either_of_3rd_4th_or_5th()

except Exception:
   handle_all_other_exceptions()

请参阅:http : //docs.python.org/tutorial/errors.html

关键字“ as”用于将错误分配给变量,以便稍后可以在代码中更彻底地调查错误。另请注意,在python 3中需要三重异常情况的括号。此页面具有更多信息:在一行中捕获多个异常(块除外)

Yes, it is possible.

try:
   ...
except FirstException:
   handle_first_one()

except SecondException:
   handle_second_one()

except (ThirdException, FourthException, FifthException) as e:
   handle_either_of_3rd_4th_or_5th()

except Exception:
   handle_all_other_exceptions()

See: http://docs.python.org/tutorial/errors.html

The “as” keyword is used to assign the error to a variable so that the error can be investigated more thoroughly later on in the code. Also note that the parentheses for the triple exception case are needed in python 3. This page has more info: Catch multiple exceptions in one line (except block)


在Python中记录未捕获的异常

问题:在Python中记录未捕获的异常

您如何导致未捕获的异常通过logging模块而不是通过模块输出stderr

我意识到最好的方法是:

try:
    raise Exception, 'Throwing a boring exception'
except Exception, e:
    logging.exception(e)

但是我的情况是,如果在没有捕获到异常的情况下自动调用它,那将非常好logging.exception(...)

How do you cause uncaught exceptions to output via the logging module rather than to stderr?

I realize the best way to do this would be:

try:
    raise Exception, 'Throwing a boring exception'
except Exception, e:
    logging.exception(e)

But my situation is such that it would be really nice if logging.exception(...) were invoked automatically whenever an exception isn’t caught.


回答 0

正如Ned所指出的,sys.excepthook每次引发并捕获异常时都会调用它。实际的含义是,您可以在代码中覆盖的默认行为,sys.excepthook以执行所需的任何操作(包括使用logging.exception)。

作为一个稻草人的例子:

>>> import sys
>>> def foo(exctype, value, tb):
...     print 'My Error Information'
...     print 'Type:', exctype
...     print 'Value:', value
...     print 'Traceback:', tb
... 

覆写sys.excepthook

>>> sys.excepthook = foo

提交明显的语法错误(忽略冒号)并获取自定义错误信息:

>>> def bar(a, b)
My Error Information
Type: <type 'exceptions.SyntaxError'>
Value: invalid syntax (<stdin>, line 1)
Traceback: None

有关更多信息sys.excepthookhttp : //docs.python.org/library/sys.html#sys.excepthook

As Ned pointed out, sys.excepthook is invoked every time an exception is raised and uncaught. The practical implication of this is that in your code you can override the default behavior of sys.excepthook to do whatever you want (including using logging.exception).

As a straw man example:

>>> import sys
>>> def foo(exctype, value, tb):
...     print 'My Error Information'
...     print 'Type:', exctype
...     print 'Value:', value
...     print 'Traceback:', tb
... 

Override sys.excepthook:

>>> sys.excepthook = foo

Commit obvious syntax error (leave out the colon) and get back custom error information:

>>> def bar(a, b)
My Error Information
Type: <type 'exceptions.SyntaxError'>
Value: invalid syntax (<stdin>, line 1)
Traceback: None

For more information about sys.excepthook, read the docs.


回答 1

这是一个完整的小示例,其中还包括其他一些技巧:

import sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

if __name__ == "__main__":
    raise RuntimeError("Test unhandled")
  • 忽略KeyboardInterrupt,以便控制台python程序可以使用Ctrl + C退出。

  • 完全依靠python的日志记录模块来格式化异常。

  • 将自定义记录器与示例处理程序一起使用。这将未处理的异常更改为转到stdout而不是stderr,但是您可以将相同样式的各种处理程序添加到logger对象。

Here’s a complete small example that also includes a few other tricks:

import sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

if __name__ == "__main__":
    raise RuntimeError("Test unhandled")
  • Ignore KeyboardInterrupt so a console python program can exit with Ctrl + C.

  • Rely entirely on python’s logging module for formatting the exception.

  • Use a custom logger with an example handler. This one changes the unhandled exception to go to stdout rather than stderr, but you could add all sorts of handlers in this same style to the logger object.


回答 2

sys.excepthook如果未捕获到异常,则将调用该方法:http : //docs.python.org/library/sys.html#sys.excepthook

当引发并捕获异常时,解释器使用三个参数(异常类,异常实例和回溯对象)调用sys.excepthook。在交互式会话中,这恰好在控制权返回到提示之前发生。在Python程序中,这恰好在程序退出之前发生。可以通过为sys.excepthook分配另一个三参数函数来定制此类顶级异常的处理。

The method sys.excepthook will be invoked if an exception is uncaught: http://docs.python.org/library/sys.html#sys.excepthook

When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.


回答 3

为什么不:

import sys
import logging
import traceback

def log_except_hook(*exc_info):
    text = "".join(traceback.format_exception(*exc_info))
    logging.error("Unhandled exception: %s", text)

sys.excepthook = log_except_hook

None()

这是sys.excepthook上面的输出:

$ python tb.py
ERROR:root:Unhandled exception: Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

这是带有sys.excepthook注释掉的输出:

$ python tb.py
Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

唯一的区别是前者ERROR:root:Unhandled exception:在第一行的开头。

Why not:

import sys
import logging
import traceback

def log_except_hook(*exc_info):
    text = "".join(traceback.format_exception(*exc_info))
    logging.error("Unhandled exception: %s", text)

sys.excepthook = log_except_hook

None()

Here is the output with sys.excepthook as seen above:

$ python tb.py
ERROR:root:Unhandled exception: Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

Here is the output with the sys.excepthook commented out:

$ python tb.py
Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

The only difference is that the former has ERROR:root:Unhandled exception: at the beginning of the first line.


回答 4

以Jacinda的答案为基础,但使用记录器对象:

def catchException(logger, typ, value, traceback):
    logger.critical("My Error Information")
    logger.critical("Type: %s" % typ)
    logger.critical("Value: %s" % value)
    logger.critical("Traceback: %s" % traceback)

# Use a partially applied function
func = lambda typ, value, traceback: catchException(logger, typ, value, traceback)
sys.excepthook = func

To build on Jacinda’s answer, but using a logger object:

def catchException(logger, typ, value, traceback):
    logger.critical("My Error Information")
    logger.critical("Type: %s" % typ)
    logger.critical("Value: %s" % value)
    logger.critical("Traceback: %s" % traceback)

# Use a partially applied function
func = lambda typ, value, traceback: catchException(logger, typ, value, traceback)
sys.excepthook = func

回答 5

将您的应用程序入口调用包装在一个try...except块中,这样您就可以捕获和记录(甚至重新引发)所有未捕获的异常。例如:

if __name__ == '__main__':
    main()

做这个:

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise

Wrap your app entry call in a try...except block so you’ll be able to catch and log (and perhaps re-raise) all uncaught exceptions. E.g. instead of:

if __name__ == '__main__':
    main()

Do this:

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise

回答 6

也许您可以在模块顶部执行一些操作,将stderr重定向到文件,然后在底部登录该文件

sock = open('error.log', 'w')               
sys.stderr = sock

doSomething() #makes errors and they will log to error.log

logging.exception(open('error.log', 'r').read() )

Maybe you could do something at the top of a module that redirects stderr to a file, and then logg that file at the bottom

sock = open('error.log', 'w')               
sys.stderr = sock

doSomething() #makes errors and they will log to error.log

logging.exception(open('error.log', 'r').read() )

回答 7

尽管@gnu_lorien的回答为我提供了一个很好的起点,但我的程序在发生第一次异常时崩溃。

我提供了一个定制的(和/或)改进的解决方案,该解决方案以静默方式记录了以修饰的函数的异常@handle_error

import logging

__author__ = 'ahmed'
logging.basicConfig(filename='error.log', level=logging.DEBUG)


def handle_exception(exc_type, exc_value, exc_traceback):
    import sys
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logging.critical(exc_value.message, exc_info=(exc_type, exc_value, exc_traceback))


def handle_error(func):
    import sys

    def __inner(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception, e:
            exc_type, exc_value, exc_tb = sys.exc_info()
            handle_exception(exc_type, exc_value, exc_tb)
        finally:
            print(e.message)
    return __inner


@handle_error
def main():
    raise RuntimeError("RuntimeError")


if __name__ == "__main__":
    for _ in xrange(1, 20):
        main()

Although @gnu_lorien’s answer gave me good starting point, my program crashes on first exception.

I came with a customised (and/or) improved solution, which silently logs Exceptions of functions that are decorated with @handle_error.

import logging

__author__ = 'ahmed'
logging.basicConfig(filename='error.log', level=logging.DEBUG)


def handle_exception(exc_type, exc_value, exc_traceback):
    import sys
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logging.critical(exc_value.message, exc_info=(exc_type, exc_value, exc_traceback))


def handle_error(func):
    import sys

    def __inner(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception, e:
            exc_type, exc_value, exc_tb = sys.exc_info()
            handle_exception(exc_type, exc_value, exc_tb)
        finally:
            print(e.message)
    return __inner


@handle_error
def main():
    raise RuntimeError("RuntimeError")


if __name__ == "__main__":
    for _ in xrange(1, 20):
        main()

回答 8

为了回答已接受答案的注释部分中讨论的宙斯先生的问题,我使用它在交互式控制台中记录未捕获的异常(已通过PyCharm 2018-2019测试)。我发现sys.excepthook它在python shell中不起作用,因此我更深入地研究并发现可以使用它sys.exc_info。但是,sys.exc_info不像其他任何参数sys.excepthook接受3个参数不同。

在这里,我同时使用sys.excepthooksys.exc_info在交互式控制台和带有包装函数的脚本中记录这两个异常。要将挂钩函数附加到这两个函数,我有两个不同的接口,具体取决于是否给定了参数。

这是代码:

def log_exception(exctype, value, traceback):
    logger.error("Uncaught exception occurred!",
                 exc_info=(exctype, value, traceback))


def attach_hook(hook_func, run_func):
    def inner(*args, **kwargs):
        if not (args or kwargs):
            # This condition is for sys.exc_info
            local_args = run_func()
            hook_func(*local_args)
        else:
            # This condition is for sys.excepthook
            hook_func(*args, **kwargs)
        return run_func(*args, **kwargs)
    return inner


sys.exc_info = attach_hook(log_exception, sys.exc_info)
sys.excepthook = attach_hook(log_exception, sys.excepthook)

日志设置可以在gnu_lorien的答案中找到。

To answer the question from Mr.Zeus discussed in the comment section of the accepted answer, I use this to log uncaught exceptions in an interactive console (tested with PyCharm 2018-2019). I found out sys.excepthook does not work in a python shell so I looked deeper and found that I could use sys.exc_info instead. However, sys.exc_info takes no arguments unlike sys.excepthook that takes 3 arguments.

Here, I use both sys.excepthook and sys.exc_info to log both exceptions in an interactive console and a script with a wrapper function. To attach a hook function to both functions, I have two different interfaces depending if arguments are given or not.

Here’s the code:

def log_exception(exctype, value, traceback):
    logger.error("Uncaught exception occurred!",
                 exc_info=(exctype, value, traceback))


def attach_hook(hook_func, run_func):
    def inner(*args, **kwargs):
        if not (args or kwargs):
            # This condition is for sys.exc_info
            local_args = run_func()
            hook_func(*local_args)
        else:
            # This condition is for sys.excepthook
            hook_func(*args, **kwargs)
        return run_func(*args, **kwargs)
    return inner


sys.exc_info = attach_hook(log_exception, sys.exc_info)
sys.excepthook = attach_hook(log_exception, sys.excepthook)

The logging setup can be found in gnu_lorien’s answer.


回答 9

就我而言,python 3在使用@Jacinda的答案时(使用)没有打印回溯的内容。相反,它只是打印对象本身:<traceback object at 0x7f90299b7b90>

相反,我这样做:

import sys
import logging
import traceback

def custom_excepthook(exc_type, exc_value, exc_traceback):
    # Do not print exception when user cancels the program
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logging.error("An uncaught exception occurred:")
    logging.error("Type: %s", exc_type)
    logging.error("Value: %s", exc_value)

    if exc_traceback:
        format_exception = traceback.format_tb(exc_traceback)
        for line in format_exception:
            logging.error(repr(line))

sys.excepthook = custom_excepthook

In my case (using python 3) when using @Jacinda ‘s answer the content of the traceback was not printed. Instead, it just prints the object itself: <traceback object at 0x7f90299b7b90>.

Instead I do:

import sys
import logging
import traceback

def custom_excepthook(exc_type, exc_value, exc_traceback):
    # Do not print exception when user cancels the program
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logging.error("An uncaught exception occurred:")
    logging.error("Type: %s", exc_type)
    logging.error("Value: %s", exc_value)

    if exc_traceback:
        format_exception = traceback.format_tb(exc_traceback)
        for line in format_exception:
            logging.error(repr(line))

sys.excepthook = custom_excepthook

为什么我们在Python中需要“ finally”子句?

问题:为什么我们在Python中需要“ finally”子句?

我不知道为什么我们需要finallytry...except...finally报表。我认为,此代码块

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

使用finally以下命令与此相同:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

我想念什么吗?

I am not sure why we need finally in try...except...finally statements. In my opinion, this code block

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

is the same with this one using finally:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

Am I missing something?


回答 0

如果您提早返回,会有所不同:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

比较一下:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

可能导致差异的其他情况:

  • 如果在except块内引发异常。
  • 如果引发异常,run_code1()但不是TypeError
  • 其他控制流语句,例如continuebreak语句。

It makes a difference if you return early:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

Compare to this:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

Other situations that can cause differences:

  • If an exception is thrown inside the except block.
  • If an exception is thrown in run_code1() but it’s not a TypeError.
  • Other control flow statements such as continue and break statements.

回答 1

您可以finally用来确保文件或资源被关闭或释放,而不管是否发生异常,即使您没有捕获到该异常也是如此。(或者,如果您没有捕获到该特定异常。)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

在此示例中,使用该with语句会更好,但是这种结构可以用于其他类型的资源。

几年后,我写了一篇博客文章,讲述滥用finally读者可能会觉得有趣的事情。

You can use finally to make sure files or resources are closed or released regardless of whether an exception occurs, even if you don’t catch the exception. (Or if you don’t catch that specific exception.)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

In this example you’d be better off using the with statement, but this kind of structure can be used for other kinds of resources.

A few years later, I wrote a blog post about an abuse of finally that readers may find amusing.


回答 2

它们不相等。不管其他什么情况发生,最终代码都会运行。这对于必须运行的清理代码很有用。

They are not equivalent. Finally code is run no matter what else happens. It is useful for cleanup code that has to run.


回答 3

除了上面的其他答案外,该finally子句无论执行什么都将else执行,而该子句仅在未引发异常的情况下才执行。

例如,无exceptions地写入文件将输出以下内容:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

输出:

Writing to file.
Write successful.
File closed.

如果有异常,代码将输出以下内容(请注意,故意使文件保持只读状态会导致错误。

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

输出:

Could not write to file.
File closed.

我们可以看到,该finally子句无论有无异常都会执行。希望这可以帮助。

To add to the other answers above, the finally clause executes no matter what whereas the else clause executes only if an exception was not raised.

For example, writing to a file with no exceptions will output the following:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

OUTPUT:

Writing to file.
Write successful.
File closed.

If there is an exception, the code will output the following, (note that a deliberate error is caused by keeping the file read-only.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

OUTPUT:

Could not write to file.
File closed.

We can see that the finally clause executes regardless of an exception. Hope this helps.


回答 4

代码块不是等效的。finally如果run_code1()引发除以外的异常TypeError,或者run_code2()引发异常,则该子句也将运行,而other_code()在这些情况下,在第一个版本中则不会运行该子句。

The code blocks are not equivalent. The finally clause will also be run if run_code1() throws an exception other than TypeError, or if run_code2() throws an exception, while other_code() in the first version wouldn’t be run in these cases.


回答 5

在您的第一个示例中,如果run_code1()引发一个不是的异常会发生什么TypeError?… other_code()将不会被执行。

将其与finally:版本进行比较:other_code()保证无论是否引发任何异常都将被执行。

In your first example, what happens if run_code1() raises an exception that is not TypeError? … other_code() will not be executed.

Compare that with the finally: version: other_code() is guaranteed to be executed regardless of any exception being raised.


回答 6

文档中所述,该finally子句旨在定义在所有情况下都必须执行的清理操作。

如果finally存在,则指定“清理”处理程序。该try 子句被执行,包括any exceptelse子句。如果在任何子句中发生异常并且未进行处理,则将临时保存该异常。该finally子句被执行。如果存在已保存的异常,则会在finally 子句末重新引发。

一个例子:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

如您所见,该finally子句在任何情况下都会执行。在TypeError通过将两个字符串凸起不被处理except子句和后因此再次加注finally条款已经被执行。

在实际的应用程序中,无论是否成功使用资源,finally子句对于释放外部资源(例如文件或网络连接)都是有用的。

As explained in the documentation, the finally clause is intended to define clean-up actions that must be executed under all circumstances.

If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause.

An example:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

As you can see, the finally clause is executed in any event. The TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.

In real world applications, the finally clause is useful for releasing external resources (such as files or network connections), regardless of whether the use of the resource was successful.


回答 7

完美的例子如下:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)

Perfect example is as below:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)

回答 8

finally用于定义“清理动作”finally在离开try语句之前的任何事件中,无论是否发生异常(即使您不处理它),都将执行该子句。

我第二个@Byers的例子。

finally is for defining “clean up actions”. The finally clause is executed in any event before leaving the try statement, whether an exception (even if you do not handle it) has occurred or not.

I second @Byers’s example.


回答 9

当您要在运行主要工作的代码之前运行“可选”代码并且可选代码可能由于各种原因而失败时,也可以使用Final。

在下面的示例中,我们不确切知道store_some_debug_info可能会引发哪种异常。

我们可以运行:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

但是,大多数的短毛猫都会抱怨捉摸不清一个exceptions。另外,由于我们选择仅pass针对错误,因此该except块并没有真正增加价值。

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

上面的代码与第一个代码块具有相同的效果,但更为简洁。

Finally can also be used when you want to run “optional” code before running the code for your main work and that optional code may fail for various reasons.

In the following example, we don’t know precisely what kind of exceptions store_some_debug_info might throw.

We could run:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

But, most linters will complain about catching too vague of an exception. Also, since we’re choosing to just pass for errors, the except block doesn’t really add value.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

The above code has the same effect as the 1st block of code but is more concise.


回答 10

几年来专业地使用delphi教会了我维护最终使用的清理例程。Delphi几乎会强制使用finally来清理在try块之前创建的所有资源,以免引起内存泄漏。这也是Java,Python和Ruby的工作方式。

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

不管您在尝试和最终之间进行什么操作,资源都会被清理。另外,如果执行从未到达该try块,则不会清除它。(即create_resource本身引发异常),这使您的代码“异常安全”。

至于为什么您实际上需要一个finally块,并不是所有语言都需要。在C ++中,您自动调用了析构函数,这些析构函数在异常展开堆栈时强制执行清除。与尝试…最终的语言相比,我认为这是朝着更清洁的代码方向发展的一步。

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.

Using delphi professionally for some years taught me to safeguard my cleanup routines using finally. Delphi pretty much enforces the use of finally to clean up any resources created before the try block, lest you cause a memory leak. This is also how Java, Python and Ruby works.

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

and resource will be cleaned up regardless of what you do between try and finally. Also, it won’t be cleaned up if execution never reaches the try block. (i.e. create_resource itself throws an exception) It makes your code “exception safe”.

As to why you actually need a finally block, not all languages do. In C++ where you have automatically called destructors which enforce cleanup when an exception unrolls the stack. I think this is a step up in the direction of cleaner code compared to try…finally languages.

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.

回答 11

一个try块只有一个强制性子句:try语句。else,else和finally子句是可选的,并且基于用户首选项。

最终:在Python离开try语句之前,它将在任何情况下在finally块中运行代码,即使它正在结束程序。例如,如果Python在except或else块中运行代码时遇到错误,则在停止程序之前,finally块仍将执行。

A try block has just one mandatory clause: The try statement. The except, else and finally clauses are optional and based on user preference.

finally: Before Python leaves the try statement, it will run the code in the finally block under any conditions, even if it’s ending the program. E.g., if Python ran into an error while running code in the except or else block, the finally block will still be executed before stopping the program.


回答 12

运行以下Python3代码以最终了解最终需求:

情况1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

案例2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

每次尝试以下输入:

  1. 随机整数
  2. 正确的代码是586(尝试此操作,您将得到答案)
  3. 随机字符串

**在学习Python的初期。

Run these Python3 codes to watch the need of finally:

CASE1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

CASE2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

Try the following inputs each time:

  1. random integers
  2. correct code which is 586(Try this and you will get your answer)
  3. random strings

** At a very early stage of learning Python.


回答 13

我试图在要阅读Excel工作表的地方运行代码。问题是,如果有一个没有工作表的文件说:SheetSum我无法将其移动到错误位置!我写的代码是:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

给出错误:

[WinError 32]该进程无法访问文件,因为该文件正在被另一个进程使用

我必须添加完整的try except with finally块并告诉finally我在任何情况下都需要关闭文件:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

否则,文件仍然保持打开状态。

如果finally存在,则指定清除处理程序。该try 子句被执行,包括any exceptelse子句。如果在任何子句中发生异常并且未对其进行处理,则将临时保存异常。该finally子句被执行。如果存在已保存的异常,则会在finally 子句末重新引发。如果finally子句引发另一个异常,则将保存的异常设置为新异常的上下文。

..更多这里

I was trying to run a code where i wanted to read excel sheets. Issue was, if there is a file which has no sheet named say : SheetSum I am not able to move it to error location!! Code i wrote was:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Giving Error :

[WinError 32] The process cannot access the file because it is being used by another process

I had to add full try except with finally block and tell finally i need to close the file in any case like:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Otherwise, file still remains open is the background.

If finally is present, it specifies a cleanup handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause. If the finally clause raises another exception, the saved exception is set as the context of the new exception.

..More Here


使用Python’with’语句时捕获异常

问题:使用Python’with’语句时捕获异常

令我感到羞耻的是,我不知道如何处理python’with’语句的异常。如果我有代码:

with open("a.txt") as f:
    print f.readlines()

我真的很想处理“找不到文件异常”以便执行某些操作。但是我不能写

with open("a.txt") as f:
    print f.readlines()
except:
    print 'oops'

而且不能写

with open("a.txt") as f:
    print f.readlines()
else:
    print 'oops'

在try / except语句中包含“ with”,否则将不起作用:不会引发异常。为了以Python方式处理“ with”语句中的失败,我该怎么办?

To my shame, I can’t figure out how to handle exception for python ‘with’ statement. If I have a code:

with open("a.txt") as f:
    print f.readlines()

I really want to handle ‘file not found exception’ in order to do somehing. But I can’t write

with open("a.txt") as f:
    print f.readlines()
except:
    print 'oops'

and can’t write

with open("a.txt") as f:
    print f.readlines()
else:
    print 'oops'

enclosing ‘with’ in a try/except statement doesn’t work else: exception is not raised. What can I do in order to process failure inside ‘with’ statement in a Pythonic way?


回答 0

from __future__ import with_statement

try:
    with open( "a.txt" ) as f :
        print f.readlines()
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
    print 'oops'

如果您希望通过公开调用与工作代码进行不同的错误处理,则可以执行以下操作:

try:
    f = open('foo.txt')
except IOError:
    print('error')
else:
    with f:
        print f.readlines()
from __future__ import with_statement

try:
    with open( "a.txt" ) as f :
        print f.readlines()
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
    print 'oops'

If you want different handling for errors from the open call vs the working code you could do:

try:
    f = open('foo.txt')
except IOError:
    print('error')
else:
    with f:
        print f.readlines()

回答 1

利用该with语句的最佳“ Pythonic”方法在PEP 343中列为示例6 ,给出了该语句的背景。

@contextmanager
def opened_w_error(filename, mode="r"):
    try:
        f = open(filename, mode)
    except IOError, err:
        yield None, err
    else:
        try:
            yield f, None
        finally:
            f.close()

用法如下:

with opened_w_error("/etc/passwd", "a") as (f, err):
    if err:
        print "IOError:", err
    else:
        f.write("guido::0:0::/:/bin/sh\n")

The best “Pythonic” way to do this, exploiting the with statement, is listed as Example #6 in PEP 343, which gives the background of the statement.

@contextmanager
def opened_w_error(filename, mode="r"):
    try:
        f = open(filename, mode)
    except IOError, err:
        yield None, err
    else:
        try:
            yield f, None
        finally:
            f.close()

Used as follows:

with opened_w_error("/etc/passwd", "a") as (f, err):
    if err:
        print "IOError:", err
    else:
        f.write("guido::0:0::/:/bin/sh\n")

回答 2

使用Python’with’语句时捕获异常

从Python 2.6开始,with语句无需__future__导入即可使用。您可以使用以下命令在Python 2.5之前获得它(但现在是时候进行升级了!):

from __future__ import with_statement

这是更正您所拥有的东西。您快到了,但是with没有except子句:

with open("a.txt") as f: 
    print(f.readlines())
except:                    # <- with doesn't have an except clause.
    print('oops')

上下文管理器的__exit__方法(如果返回)False将在完成时引发错误。如果返回True,它将抑制它。在open内置的__exit__不返回True,那么你只需要嵌套一个try,except块:

try:
    with open("a.txt") as f:
        print(f.readlines())
except Exception as error: 
    print('oops')

和标准样板:不要使用裸露的东西except:,以免发生BaseException任何其他异常和警告。至少要Exception和错误一样具体,也许要抓住IOError。只捕获您准备处理的错误。

因此,在这种情况下,您需要执行以下操作:

>>> try:
...     with open("a.txt") as f:
...         print(f.readlines())
... except IOError as error: 
...     print('oops')
... 
oops

Catching an exception while using a Python ‘with’ statement

The with statement has been available without the __future__ import since Python 2.6. You can get it as early as Python 2.5 (but at this point it’s time to upgrade!) with:

from __future__ import with_statement

Here’s the closest thing to correct that you have. You’re almost there, but with doesn’t have an except clause:

with open("a.txt") as f: 
    print(f.readlines())
except:                    # <- with doesn't have an except clause.
    print('oops')

A context manager’s __exit__ method, if it returns False will reraise the error when it finishes. If it returns True, it will suppress it. The open builtin’s __exit__ doesn’t return True, so you just need to nest it in a try, except block:

try:
    with open("a.txt") as f:
        print(f.readlines())
except Exception as error: 
    print('oops')

And standard boilerplate: don’t use a bare except: which catches BaseException and every other possible exception and warning. Be at least as specific as Exception, and for this error, perhaps catch IOError. Only catch errors you’re prepared to handle.

So in this case, you’d do:

>>> try:
...     with open("a.txt") as f:
...         print(f.readlines())
... except IOError as error: 
...     print('oops')
... 
oops

回答 3

区分复合with语句引发的异常的可能来源

区分with语句中发生的异常非常棘手,因为它们可能起源于不同的地方。可以从以下任一位置(或其中调用的功能)引发异常:

  • ContextManager.__init__
  • ContextManager.__enter__
  • 的身体 with
  • ContextManager.__exit__

有关更多详细信息,请参见有关Context Manager Types的文档。

如果我们想区分这些不同的情况,仅将包裹with到a中try .. except是不够的。考虑以下示例(ValueError用作示例,但当然可以用任何其他异常类型代替):

try:
    with ContextManager():
        BLOCK
except ValueError as err:
    print(err)

在此,except遗嘱将捕获源自四个不同地方的所有exceptions,因此不允许对其进行区分。如果将上下文管理器对象的实例移到之外with,则可以区分__init__BLOCK / __enter__ / __exit__

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        with mgr:
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        # At this point we still cannot distinguish between exceptions raised from
        # __enter__, BLOCK, __exit__ (also BLOCK since we didn't catch ValueError in the body)
        pass

有效地,这仅对__init__零件有所帮助,但是我们可以添加一个额外的哨兵变量来检查with开始执行的主体(即__enter__与其他主体区别):

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        entered_body = False
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        else:
            # At this point we know the exception came either from BLOCK or from __exit__
            pass

棘手的部分是区分源自BLOCK__exit__的异常,这是因为逃脱了withwill 主体的异常将传递给__exit__它,从而可以决定如何处理它(请参阅docs)。但是__exit__,如果出现异常,则将原来的exceptions替换为新的exceptions。为了处理这些情况,我们可以except在的主体中添加一个通用子句,with以存储可能会被忽略而不会被忽略的任何潜在异常,并将其与except后来在最外层捕获的异常进行比较-如果它们相同,则表示起源是BLOCK否则,它是__exit__(以防万一__exit__,通过在最外层返回true值来抑制异常except 根本不会执行)。

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    entered_body = exc_escaped_from_body = False
    try:
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
            except Exception as err:  # this exception would normally escape without notice
                # we store this exception to check in the outer `except` clause
                # whether it is the same (otherwise it comes from __exit__)
                exc_escaped_from_body = err
                raise  # re-raise since we didn't intend to handle it, just needed to store it
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        elif err is exc_escaped_from_body:
            print('BLOCK raised:', err)
        else:
            print('__exit__ raised:', err)

使用PEP 343中提到的等效形式的替代方法

PEP 343-“ with”语句指定该语句的等效“ non-with”版本with。在这里,我们可以轻松地将各个部分包装在一起try ... except,从而区分出不同的潜在错误源:

import sys

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        value = type(mgr).__enter__(mgr)
    except ValueError as err:
        print('__enter__ raised:', err)
    else:
        exit = type(mgr).__exit__
        exc = True
        try:
            try:
                BLOCK
            except TypeError:
                pass
            except:
                exc = False
                try:
                    exit_val = exit(mgr, *sys.exc_info())
                except ValueError as err:
                    print('__exit__ raised:', err)
                else:
                    if not exit_val:
                        raise
        except ValueError as err:
            print('BLOCK raised:', err)
        finally:
            if exc:
                try:
                    exit(mgr, None, None, None)
                except ValueError as err:
                    print('__exit__ raised:', err)

通常,更简单的方法就可以了

这种特殊异常处理的需求应该非常少,通常将整个包装with在一个try ... except块中就足够了。特别是如果各种错误源由不同的(自定义)异常类型指示(需要相应地设计上下文管理器),我们可以很容易地区分它们。例如:

try:
    with ContextManager():
        BLOCK
except InitError:  # raised from __init__
    ...
except AcquireResourceError:  # raised from __enter__
    ...
except ValueError:  # raised from BLOCK
    ...
except ReleaseResourceError:  # raised from __exit__
    ...

Differentiating between the possible origins of exceptions raised from a compound with statement

Differentiating between exceptions that occur in a with statement is tricky because they can originate in different places. Exceptions can be raised from either of the following places (or functions called therein):

  • ContextManager.__init__
  • ContextManager.__enter__
  • the body of the with
  • ContextManager.__exit__

For more details see the documentation about Context Manager Types.

If we want to distinguish between these different cases, just wrapping the with into a try .. except is not sufficient. Consider the following example (using ValueError as an example but of course it could be substituted with any other exception type):

try:
    with ContextManager():
        BLOCK
except ValueError as err:
    print(err)

Here the except will catch exceptions originating in all of the four different places and thus does not allow to distinguish between them. If we move the instantiation of the context manager object outside the with, we can distinguish between __init__ and BLOCK / __enter__ / __exit__:

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        with mgr:
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        # At this point we still cannot distinguish between exceptions raised from
        # __enter__, BLOCK, __exit__ (also BLOCK since we didn't catch ValueError in the body)
        pass

Effectively this just helped with the __init__ part but we can add an extra sentinel variable to check whether the body of the with started to execute (i.e. differentiating between __enter__ and the others):

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        entered_body = False
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        else:
            # At this point we know the exception came either from BLOCK or from __exit__
            pass

The tricky part is to differentiate between exceptions originating from BLOCK and __exit__ because an exception that escapes the body of the with will be passed to __exit__ which can decide how to handle it (see the docs). If however __exit__ raises itself, the original exception will be replaced by the new one. To deal with these cases we can add a general except clause in the body of the with to store any potential exception that would have otherwise escaped unnoticed and compare it with the one caught in the outermost except later on – if they are the same this means the origin was BLOCK or otherwise it was __exit__ (in case __exit__ suppresses the exception by returning a true value the outermost except will simply not be executed).

try:
    mgr = ContextManager()  # __init__ could raise
except ValueError as err:
    print('__init__ raised:', err)
else:
    entered_body = exc_escaped_from_body = False
    try:
        with mgr:
            entered_body = True  # __enter__ did not raise at this point
            try:
                BLOCK
            except TypeError:  # catching another type (which we want to handle here)
                pass
            except Exception as err:  # this exception would normally escape without notice
                # we store this exception to check in the outer `except` clause
                # whether it is the same (otherwise it comes from __exit__)
                exc_escaped_from_body = err
                raise  # re-raise since we didn't intend to handle it, just needed to store it
    except ValueError as err:
        if not entered_body:
            print('__enter__ raised:', err)
        elif err is exc_escaped_from_body:
            print('BLOCK raised:', err)
        else:
            print('__exit__ raised:', err)

Alternative approach using the equivalent form mentioned in PEP 343

PEP 343 — The “with” Statement specifies an equivalent “non-with” version of the with statement. Here we can readily wrap the various parts with try ... except and thus differentiate between the different potential error sources:

import sys

try:
    mgr = ContextManager()
except ValueError as err:
    print('__init__ raised:', err)
else:
    try:
        value = type(mgr).__enter__(mgr)
    except ValueError as err:
        print('__enter__ raised:', err)
    else:
        exit = type(mgr).__exit__
        exc = True
        try:
            try:
                BLOCK
            except TypeError:
                pass
            except:
                exc = False
                try:
                    exit_val = exit(mgr, *sys.exc_info())
                except ValueError as err:
                    print('__exit__ raised:', err)
                else:
                    if not exit_val:
                        raise
        except ValueError as err:
            print('BLOCK raised:', err)
        finally:
            if exc:
                try:
                    exit(mgr, None, None, None)
                except ValueError as err:
                    print('__exit__ raised:', err)

Usually a simpler approach will do just fine

The need for such special exception handling should be quite rare and normally wrapping the whole with in a try ... except block will be sufficient. Especially if the various error sources are indicated by different (custom) exception types (the context managers need to be designed accordingly) we can readily distinguish between them. For example:

try:
    with ContextManager():
        BLOCK
except InitError:  # raised from __init__
    ...
except AcquireResourceError:  # raised from __enter__
    ...
except ValueError:  # raised from BLOCK
    ...
except ReleaseResourceError:  # raised from __exit__
    ...

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

问题:Python try-else

语句的可选else子句的预期用途是try什么?

What is the intended use of the optional else clause of the try statement?


回答 0

else如果执行不符合try-如果没有exceptions,则执行块中的语句。老实说,我从来没有发现需要。

但是,“ 处理异常”指出:

使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try … except语句保护的代码未引发的异常。

所以,如果你有一个方法可以,例如,抛出IOError了,你想抓住它会引发异常,但有你想,如果第一个操作成功做其它的事情,你不要想抓住从一个IOError该操作,您可能会这样写:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

如果仅放在another_operation_that_can_throw_ioerror()之后operation_that_can_throw_ioerrorexcept则将捕获第二个调用的错误。并且,如果将其放在整个代码try块之后,它将始终运行,直到finally。将else让您确保

  1. 只有在没有exceptions的情况下,第二个操作才会运行
  2. 它在代码finally块之前运行,并且
  3. IOError它筹集到的任何钱都没有被抓住

The statements in the else block are executed if execution falls off the bottom of the try – if there was no exception. Honestly, I’ve never found a need.

However, Handling Exceptions notes:

The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try … except statement.

So, if you have a method that could, for example, throw an IOError, and you want to catch exceptions it raises, but there’s something else you want to do if the first operation succeeds, and you don’t want to catch an IOError from that operation, you might write something like this:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

If you just put another_operation_that_can_throw_ioerror() after operation_that_can_throw_ioerror, the except would catch the second call’s errors. And if you put it after the whole try block, it’ll always be run, and not until after the finally. The else lets you make sure

  1. the second operation’s only run if there’s no exception,
  2. it’s run before the finally block, and
  3. any IOErrors it raises aren’t caught here

回答 1

有一个重要的用途else-风格和可读性。通常,将可能导致异常的代码保留在处理它们的代码附近是一个好主意。例如,比较这些:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

except不能提前返回或重新引发异常时,第二个是好的。如果可能的话,我会写:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

注:答案抄自最近发布的重复这里,所以这一切的“AskPassword”的东西。

There is one big reason to use else – style and readability. It’s generally a good idea to keep code that can cause exceptions near the code that deals with them. For example, compare these:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

and

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

The second one is good when the except can’t return early, or re-throw the exception. If possible, I would have written:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Note: Answer copied from recently-posted duplicate here, hence all this “AskPassword” stuff.


回答 2

一种用途:测试一些应引发异常的代码。

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(此代码应在实践中抽象为更通用的测试。)

One use: test some code that should raise an exception.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(This code should be abstracted into a more generic test in practice.)


回答 3

Python try-else

elsetry语句的optional 子句的预期用途是什么?

如果没有预期处理异常的情况,则预期的用途是为更多代码运行提供上下文。

此上下文避免了意外处理您未预期到的错误。

但重要的是要了解导致else子句来运行的精确条件,因为returncontinuebreak可中断的控制流else

综上所述

else声明是否有运行没有异常,如果不是被打断returncontinuebreak声明。

其他答案错过了最后一部分。

从文档:

可选的else,如果条款被执行,并且当控制切断端流动的的try条款。*

(加粗体。)脚注如下:

*目前,控制“落端流动”除了在异常或一个的执行的情况下returncontinuebreak语句。

它确实需要至少一个前面的except子句(请参阅语法)。因此,它实际上不是“ try-else”,而是“ try-except-else(-finally)”,else(和finally)是可选的。

Python指南预期的使用详解:

try … except语句具有可选的else子句,该子句在存在时必须遵循所有except子句。这对于try子句未引发异常的必须执行的代码很有用。例如:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try … except语句保护的代码未引发的异常。

else后面的示例区分代码try

如果处理错误,该else块将不会运行。例如:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

现在,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

Python try-else

What is the intended use of the optional else clause of the try statement?

The intended use is to have a context for more code to run if there were no exceptions where it was expected to be handled.

This context avoids accidentally handling errors you did not expect.

But it’s important to understand the precise conditions that cause the else clause to run, because return, continue, and break can interrupt the control flow to else.

In Summary

The else statement runs if there are no exceptions and if not interrupted by a return, continue, or break statement.

The other answers miss that last part.

From the docs:

The optional else clause is executed if and when control flows off the end of the try clause.*

(Bolding added.) And the footnote reads:

*Currently, control “flows off the end” except in the case of an exception or the execution of a return, continue, or break statement.

It does require at least one preceding except clause (see the grammar). So it really isn’t “try-else,” it’s “try-except-else(-finally),” with the else (and finally) being optional.

The Python Tutorial elaborates on the intended usage:

The try … except statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try … except statement.

Example differentiating else versus code following the try block

If you handle an error, the else block will not run. For example:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

And now,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

回答 4

Try-except-else非常适合将EAFP模式鸭子模式结合使用:

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

您可能会觉得此朴素的代码很好:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

这是一种意外隐藏代码中严重错误的好方法。我在这里打错了清理,但是让我知道的AttributeError被吞噬了。更糟糕的是,如果我正确编写了该方法,但是偶尔会向该传递给具有错误名称属性的用户类型的清理方法,导致该方法在中途无声地失败并且未关闭文件?祝您调试好运。

Try-except-else is great for combining the EAFP pattern with duck-typing:

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

You might thing this naïve code is fine:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

This is a great way of accidentally hiding severe bugs in your code. I typo-ed cleanup there, but the AttributeError that would let me know is being swallowed. Worse, what if I’d written it correctly, but the cleanup method was occasionally being passed a user type that had a misnamed attribute, causing it to silently fail half-way through and leave a file unclosed? Good luck debugging that one.


回答 5

我发现当您必须执行清理操作时,即使有exceptions,它也确实很有用:

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()

I find it really useful when you’ve got cleanup to do that has to be done even if there’s an exception:

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()

回答 6

即使您现在无法想到它的用途,您也可以打赌一定有它的用途。这是一个难以想象的示例:

else

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

没有else

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

something如果没有错误抛出,则在此处定义了变量。您可以在try块外将其删除,但是如果定义了变量,则需要进行一些混乱的检测。

Even though you can’t think of a use of it right now, you can bet there has to be a use for it. Here is an unimaginative sample:

With else:

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

Without else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Here you have the variable something defined if no error is thrown. You can remove this outside the try block, but then it requires some messy detection if a variable is defined.


回答 7

有一个很好的例子try-elsePEP 380。基本上,归结为在算法的不同部分进行不同的异常处理。

就像这样:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

这使您可以在发生异常的地方编写异常处理代码。

There’s a nice example of try-else in PEP 380. Basically, it comes down to doing different exception handling in different parts of the algorithm.

It’s something like this:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

This allows you to write the exception handling code nearer to where the exception occurs.


回答 8

来自错误和异常#处理异常-docs.python.org

try ... except语句有一个可选else子句,当存在时,该子句必须遵循所有除子句之外的子句。这对于try子句未引发异常的必须执行的代码很有用。例如:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try … except语句保护的代码未引发的异常。

From Errors and Exceptions # Handling exceptions – docs.python.org

The try ... except statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try … except statement.


回答 9

查看Python参考,似乎else是在try没有异常的情况下执行的。当控制从try子句的末尾流出时,将执行可选的else子句。2 else子句中的异常不由前面的except子句处理。

深入研究python有一个例子,如果我理解正确,try他们会在块中尝试导入模块,当该模块失败时,您将获得异常并绑定默认值,但是当它起作用时,您可以选择进入else块并绑定所需的内容(请参阅示例和说明的链接)。

如果您尝试在代码catch块中进行工作,则可能会引发另一个异常-我想这就是代码else块派上用场的地方。

Looking at Python reference it seems that else is executed after try when there’s no exception. The optional else clause is executed if and when control flows off the end of the try clause. 2 Exceptions in the else clause are not handled by the preceding except clauses.

Dive into python has an example where, if I understand correctly, in try block they try to import a module, when that fails you get exception and bind default but when it works you have an option to go into else block and bind what is required (see link for the example and explanation).

If you tried to do work in catch block it might throw another exception – I guess that’s where the else block comes handy.


回答 10

而已。try-except子句的’else’块存在于(仅当)tryed操作成功时运行的代码。它可以被使用,也可以被滥用。

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

我个人喜欢它,并在适当的时候使用它。它在语义上对语句进行分组。

That’s it. The ‘else’ block of a try-except clause exists for code that runs when (and only when) the tried operation succeeds. It can be used, and it can be abused.

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

Personally, I like it and use it when appropriate. It semantically groups statements.


回答 11

可能的用途可能是:

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

也许这会导致您使用过多。

Perhaps a use might be:

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

Maybe this will lead you too a use.


回答 12

我发现该try: ... else:构造在您运行数据库查询并将这些查询的结果记录到具有相同口味/类型的单独数据库中的情况下很有用。假设我有很多工作线程,它们都处理提交给队列的数据库查询

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

当然,如果您可以区分可能引发的异常,则不必使用它,但是如果对成功的一段代码做出反应的代码可能会抛出与该成功的段相同的异常,并且您不能仅仅让第二个可能的异常消失,或者在成功后立即返回(在我的情况下这将杀死线程),这确实派上了用场。

I have found the try: ... else: construct useful in the situation where you are running database queries and logging the results of those queries to a separate database of the same flavour/type. Let’s say I have lots of worker threads all handling database queries submitted to a queue

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

Of course if you can distinguish between the possible exceptions that might be thrown, you don’t have to use this, but if code reacting to a successful piece of code might throw the same exception as the successful piece, and you can’t just let the second possible exception go, or return immediately on success (which would kill the thread in my case), then this does come in handy.


回答 13

else通常可以使用一个块来补充每个except块中发生的功能。

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

在这种情况下,inconsistency_type在每个except块中都设置,以便在中的无错误情况下补充行为else

当然,我将其描述为一种模式,有一天可能会在您自己的代码中出现。在这种情况下,无论如何您只需将其设置inconsistency_type为0 try

An else block can often exist to complement functionality that occurs in every except block.

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

In this case, inconsistency_type is set in each except block, so that behaviour is complemented in the no-error case in else.

Of course, I’m describing this as a pattern that may turn up in your own code someday. In this specific case, you just set inconsistency_type to 0 before the try block anyway.


回答 14

这是我喜欢使用此模式的另一个地方:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

Here is another place where I like to use this pattern:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

回答 15

我可以想到的一种使用场景是不可预测的异常,如果再次尝试可以避免这种异常。例如,当try块中的操作涉及随机数时:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

但是,如果可以预测到异常,则应始终在选择异常之前预先选择验证。但是,并非所有事情都可以预测,因此此代码模式应有的地位。

One of the use scenarios I can think of is unpredictable exceptions, which can be circumvented if you try again. For instance, when the operations in try block involves random numbers:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

But if the exception can be predicted, you should always choose validation beforehand over an exception. However, not everything can be predicted, so this code pattern has its place.


回答 16

我发现else对于处理可能不正确的配置文件很有用:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

读取lock配置的异常会禁用锁定监视,并且ValueErrors会记录一条有用的警告消息。

I have found else useful for dealing with a possibly incorrect config file:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

An exception reading the lock config disables lock monitoring and ValueErrors log a helpful warning message.


回答 17

假设您的编程逻辑取决于字典是否具有带有给定键的条目。您可以测试dict.get(key)使用if... else...构造的结果,也可以执行以下操作:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

Suppose your programming logic depends on whether a dictionary has an entry with a given key. You can test the result of dict.get(key) using if... else... construct, or you can do:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

回答 18

我将添加另一个在处理数据库会话时似乎很简单的用例:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

I would add another use case that seems straight forward when handling DB sessions:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

回答 19

else:块令人困惑,并且(几乎)无用。它也是forand while语句的一部分。

实际上,即使在if声明中,else:也可能以真正可怕的方式滥用它们,从而创建很难发现的错误。

考虑一下。

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

再三考虑else:。这通常是一个问题。除非在if-statement中,否则应避免使用它,甚至还要考虑记录-condition else以使其明确。

The else: block is confusing and (nearly) useless. It’s also part of the for and while statements.

Actually, even on an if-statement, the else: can be abused in truly terrible ways creating bugs that are very hard to find.

Consider this.

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

Think twice about else:. It is generally a problem. Avoid it except in an if-statement and even then consider documenting the else– condition to make it explicit.


如何记录带有调试信息的Python错误?

问题:如何记录带有调试信息的Python错误?

我正在使用以下命令将Python异常消息打印到日志文件中logging.error

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

除了异常字符串以外,是否可以打印有关异常及其生成代码的更多详细信息?行号或堆栈跟踪之类的东西会很棒。

I am printing Python exception messages to a log file with logging.error:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

Is it possible to print more detailed information about the exception and the code that generated it than just the exception string? Things like line numbers or stack traces would be great.


回答 0

logger.exception 将在错误消息旁边输出堆栈跟踪。

例如:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

输出:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Check指出:“请注意,在Python 3中,您必须logging.exceptionexcept零件内部调用该方法。如果在任意位置调用此方法,则可能会遇到奇怪的异常。文档对此有所提示。”

logger.exception will output a stack trace alongside the error message.

For example:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

Output:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Cheque notes, “be aware that in Python 3 you must call the logging.exception method just inside the except part. If you call this method in an arbitrary place you may get a bizarre exception. The docs alert about that.”


回答 1

约一个好处logging.exceptionSiggyF的回答并没有显示的是,你可以在任意的消息传递和记录仍然会显示完整的回溯与所有异常的详细信息:

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

在默认情况下(在最新版本中),仅将错误打印到的日志记录行为sys.stderr如下所示:

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

One nice thing about logging.exception that SiggyF’s answer doesn’t show is that you can pass in an arbitrary message, and logging will still show the full traceback with all the exception details:

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

With the default (in recent versions) logging behaviour of just printing errors to sys.stderr, it looks like this:

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

回答 2

使用exc_info选项可能更好,允许您选择错误级别(如果使用exception,它将始终处于错误error级别):

try:
    # do something here
except Exception as e:
    logging.critical(e, exc_info=True)  # log exception info at CRITICAL log level

Using exc_info options may be better, to allow you to choose the error level (if you use exception, it will always be at the error level):

try:
    # do something here
except Exception as e:
    logging.critical(e, exc_info=True)  # log exception info at CRITICAL log level

回答 3

报价单

如果您的应用程序以其他方式记录日志而不使用logging模块怎么办?

现在,traceback可以在这里使用。

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Python 2中使用它:

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
  • Python 3中使用它:

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)

Quoting

What if your application does logging some other way – not using the logging module?

Now, traceback could be used here.

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Use it in Python 2:

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
    
  • Use it in Python 3:

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)
    

回答 4

如果您使用纯日志-您的所有日志记录都应符合以下规则:one record = one line。遵循此规则,您可以使用grep和其他工具来处理日志文件。

但是回溯信息是多行的。因此,我的答案是zangw在此线程中提出的解决方案的扩展版本。问题是回溯线可能在\n内部,因此我们需要做一些额外的工作来消除该行的结尾:

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

之后(当您要分析日志时),您可以从日志文件中复制/粘贴所需的回溯行,然后执行以下操作:

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

利润!

If you use plain logs – all your log records should correspond this rule: one record = one line. Following this rule you can use grep and other tools to process your log files.

But traceback information is multi-line. So my answer is an extended version of solution proposed by zangw above in this thread. The problem is that traceback lines could have \n inside, so we need to do an extra work to get rid of this line endings:

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

After that (when you’ll be analyzing your logs) you could copy / paste required traceback lines from your log file and do this:

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

Profit!


回答 5

这个答案是建立在上述优秀答案之上的。

在大多数应用程序中,您不会直接调用logging.exception(e)。您很可能已经定义了特定于您的应用程序或模块的自定义记录器,如下所示:

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

在这种情况下,只需使用记录器调用异常(e),如下所示:

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)

This answer builds up from the above excellent ones.

In most applications, you won’t be calling logging.exception(e) directly. Most likely you have defined a custom logger specific for your application or module like this:

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

In this case, just use the logger to call the exception(e) like this:

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)

回答 6

您可以毫无exceptions地记录堆栈跟踪。

https://docs.python.org/3/library/logging.html#logging.Logger.debug

第二个可选的关键字参数是stack_info,默认为False。如果为true,则将堆栈信息添加到日志消息中,包括实际的日志调用。请注意,这与通过指定exc_info显示的堆栈信息不同:前者是从堆栈底部到当前线程中的日志记录调用的堆栈帧,而后者是有关已取消缠绕的堆栈帧的信息,在搜索异常处理程序时跟踪异常。

例:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.getLogger().info('This prints the stack', stack_info=True)
INFO:root:This prints the stack
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
>>>

You can log the stack trace without an exception.

https://docs.python.org/3/library/logging.html#logging.Logger.debug

The second optional keyword argument is stack_info, which defaults to False. If true, stack information is added to the logging message, including the actual logging call. Note that this is not the same stack information as that displayed through specifying exc_info: The former is stack frames from the bottom of the stack up to the logging call in the current thread, whereas the latter is information about stack frames which have been unwound, following an exception, while searching for exception handlers.

Example:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.getLogger().info('This prints the stack', stack_info=True)
INFO:root:This prints the stack
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
>>>

回答 7

一点点装饰器处理(受Maybe monad和举重的启发很松散)。您可以安全地删除Python 3.6类型注释,并使用较旧的消息格式样式。

fallable.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

演示:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

您还可以修改此解决方案来回报比的东西更有意义一点Noneexcept部分(甚至使溶液一般,通过指定该返回值fallible的论点)。

A little bit of decorator treatment (very loosely inspired by the Maybe monad and lifting). You can safely remove Python 3.6 type annotations and use an older message formatting style.

fallible.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

Demo:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

You can also modify this solution to return something a bit more meaningful than None from the except part (or even make the solution generic, by specifying this return value in fallible‘s arguments).


回答 8

在您的日志记录模块(如果是自定义模块)中,只需启用stack_info即可。

api_logger.exceptionLog("*Input your Custom error message*",stack_info=True)

In your logging module(if custom module) just enable stack_info.

api_logger.exceptionLog("*Input your Custom error message*",stack_info=True)

回答 9

如果您可以处理额外的依赖关系,则可以使用twisted.log,您不必显式记录错误,而且它会将整个回溯和时间返回到文件或流。

If you can cope with the extra dependency then use twisted.log, you don’t have to explicitly log errors and also it returns the entire traceback and time to the file or stream.


回答 10

一种干净的方法是使用format_exc(),然后解析输出以获取相关部分:

from traceback import format_exc

try:
    1/0
except Exception:
    print 'the relevant part is: '+format_exc().split('\n')[-2]

问候

A clean way to do it is using format_exc() and then parse the output to get the relevant part:

from traceback import format_exc

try:
    1/0
except Exception:
    print 'the relevant part is: '+format_exc().split('\n')[-2]

Regards


在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.


获取引起异常的异常描述和堆栈跟踪,全部作为字符串

问题:获取引起异常的异常描述和堆栈跟踪,全部作为字符串

我看过很多关于Python中堆栈跟踪和异常的文章。但是还没有找到我所需要的。

我有一段Python 2.7代码可能会引发异常。我想捕获它并将其完整描述和导致错误的堆栈跟踪分配给字符串(只是我们在控制台上看到的所有内容)。我需要此字符串以将其打印到GUI中的文本框中。

像这样:

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print_to_textbox(complete_exception_description(e))

问题是:函数什么complete_exception_description

I’ve seen a lot of posts about stack trace and exceptions in Python. But haven’t found what I need.

I have a chunk of Python 2.7 code that may raise an exception. I would like to catch it and assign to a string its full description and the stack trace that caused the error (simply all we use to see on the console). I need this string to print it to a text box in the GUI.

Something like this:

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print_to_textbox(complete_exception_description(e))

The problem is: what is the function complete_exception_description?


回答 0

请参阅traceback模块,特别是format_exc()功能。在这里

import traceback

try:
    raise ValueError
except ValueError:
    tb = traceback.format_exc()
else:
    tb = "No error"
finally:
    print tb

See the traceback module, specifically the format_exc() function. Here.

import traceback

try:
    raise ValueError
except ValueError:
    tb = traceback.format_exc()
else:
    tb = "No error"
finally:
    print tb

回答 1

让我们创建一个相当复杂的堆栈跟踪,以证明我们获得了完整的堆栈跟踪:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

记录完整的堆栈跟踪

最佳做法是为模块设置一个记录器。它将知道模块的名称,并能够更改级别(在其他属性(例如处理程序)中)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

我们可以使用此记录器来获取错误:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

哪个日志:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

因此,我们得到与发生错误时相同的输出:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

仅获取字符串

如果您真的只想要字符串,请改用traceback.format_exc函数,在此处演示如何记录字符串:

import traceback
try:
    do_something_that_might_error()
except Exception as error:
    just_the_string = traceback.format_exc()
    logger.debug(just_the_string)

哪个日志:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Let’s create a decently complicated stacktrace, in order to demonstrate that we get the full stacktrace:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Logging the full stacktrace

A best practice is to have a logger set up for your module. It will know the name of the module and be able to change levels (among other attributes, such as handlers)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

And we can use this logger to get the error:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

Which logs:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

And so we get the same output as when we have an error:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Getting just the string

If you really just want the string, use the traceback.format_exc function instead, demonstrating logging the string here:

import traceback
try:
    do_something_that_might_error()
except Exception as error:
    just_the_string = traceback.format_exc()
    logger.debug(just_the_string)

Which logs:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

回答 2

对于Python 3,以下代码将Exception完全按照使用traceback.format_exc()以下命令获得的格式格式化对象:

import traceback

try: 
    method_that_can_raise_an_exception(params)
except Exception as ex:
    print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))

优点是仅Exception需要对象(由于记录的__traceback__属性),因此可以更轻松地将其作为参数传递给另一个函数以进行进一步处理。

With Python 3, the following code will format an Exception object exactly as would be obtained using traceback.format_exc():

import traceback

try: 
    method_that_can_raise_an_exception(params)
except Exception as ex:
    print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))

The advantage being that only the Exception object is needed (thanks to the recorded __traceback__ attribute), and can therefore be more easily passed as an argument to another function for further processing.


回答 3

>>> import sys
>>> import traceback
>>> try:
...   5 / 0
... except ZeroDivisionError as e:
...   type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
['  File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

您使用sys.exc_info()来收集信息和traceback模块中的函数以对其进行格式化。 以下是一些格式化示例。

整个异常字符串位于:

>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']
>>> import sys
>>> import traceback
>>> try:
...   5 / 0
... except ZeroDivisionError as e:
...   type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
['  File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

You use sys.exc_info() to collect the information and the functions in the traceback module to format it. Here are some examples for formatting it.

The whole exception string is at:

>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']

回答 4

对于那些使用Python-3的人

使用tracebackmodule和exception.__traceback__one可以提取堆栈跟踪,如下所示:

  • 使用获取当前的堆栈跟踪traceback.extract_stack()
  • 删除最后三个元素(因为那些是使我进入调试功能的堆栈中的条目)
  • __traceback__使用以下方法从异常对象追加traceback.extract_tb()
  • 使用格式化整个东西 traceback.format_list()
import traceback
def exception_to_string(excp):
   stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__)  # add limit=?? 
   pretty = traceback.format_list(stack)
   return ''.join(pretty) + '\n  {} {}'.format(excp.__class__,excp)

一个简单的演示:

def foo():
    try:
        something_invalid()
    except Exception as e:
        print(exception_to_string(e))

def bar():
    return foo()

调用时,将得到以下输出bar()

  File "./test.py", line 57, in <module>
    bar()
  File "./test.py", line 55, in bar
    return foo()
  File "./test.py", line 50, in foo
    something_invalid()

  <class 'NameError'> name 'something_invalid' is not defined

For those using Python-3

Using traceback module and exception.__traceback__ one can extract the stack-trace as follows:

  • grab the current stack-trace using traceback.extract_stack()
  • remove the last three elements (as those are entries in the stack that got me to my debug function)
  • append the __traceback__ from the exception object using traceback.extract_tb()
  • format the whole thing using traceback.format_list()
import traceback
def exception_to_string(excp):
   stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__)  # add limit=?? 
   pretty = traceback.format_list(stack)
   return ''.join(pretty) + '\n  {} {}'.format(excp.__class__,excp)

A simple demonstration:

def foo():
    try:
        something_invalid()
    except Exception as e:
        print(exception_to_string(e))

def bar():
    return foo()

We get the following output when we call bar():

  File "./test.py", line 57, in <module>
    bar()
  File "./test.py", line 55, in bar
    return foo()
  File "./test.py", line 50, in foo
    something_invalid()

  <class 'NameError'> name 'something_invalid' is not defined

回答 5

您可能还会考虑使用内置的Python模块cgitb来获取一些非常好且格式正确的异常信息,包括局部变量值,源代码上下文,函数参数等。

例如此代码…

import cgitb
cgitb.enable(format='text')

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    return func2(a, c)

func1(1, 5)

我们得到这个异常输出…

ZeroDivisionError
Python 3.4.2: C:\tools\python\python.exe
Tue Sep 22 15:29:33 2015

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 c:\TEMP\cgittest2.py in <module>()
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
func1 = <function func1>

 c:\TEMP\cgittest2.py in func1(a=1, b=5)
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
global func2 = <function func2>
a = 1
c = 0

 c:\TEMP\cgittest2.py in func2(a=1, divisor=0)
    3
    4 def func2(a, divisor):
    5   return a / divisor
    6
    7 def func1(a, b):
a = 1
divisor = 0
ZeroDivisionError: division by zero
    __cause__ = None
    __class__ = <class 'ZeroDivisionError'>
    __context__ = None
    __delattr__ = <method-wrapper '__delattr__' of ZeroDivisionError object>
    __dict__ = {}
    __dir__ = <built-in method __dir__ of ZeroDivisionError object>
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __eq__ = <method-wrapper '__eq__' of ZeroDivisionError object>
    __format__ = <built-in method __format__ of ZeroDivisionError object>
    __ge__ = <method-wrapper '__ge__' of ZeroDivisionError object>
    __getattribute__ = <method-wrapper '__getattribute__' of ZeroDivisionError object>
    __gt__ = <method-wrapper '__gt__' of ZeroDivisionError object>
    __hash__ = <method-wrapper '__hash__' of ZeroDivisionError object>
    __init__ = <method-wrapper '__init__' of ZeroDivisionError object>
    __le__ = <method-wrapper '__le__' of ZeroDivisionError object>
    __lt__ = <method-wrapper '__lt__' of ZeroDivisionError object>
    __ne__ = <method-wrapper '__ne__' of ZeroDivisionError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of ZeroDivisionError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of ZeroDivisionError object>
    __repr__ = <method-wrapper '__repr__' of ZeroDivisionError object>
    __setattr__ = <method-wrapper '__setattr__' of ZeroDivisionError object>
    __setstate__ = <built-in method __setstate__ of ZeroDivisionError object>
    __sizeof__ = <built-in method __sizeof__ of ZeroDivisionError object>
    __str__ = <method-wrapper '__str__' of ZeroDivisionError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __suppress_context__ = False
    __traceback__ = <traceback object>
    args = ('division by zero',)
    with_traceback = <built-in method with_traceback of ZeroDivisionError object>

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgittest2.py", line 11, in <module>
    func1(1, 5)
  File "cgittest2.py", line 9, in func1
    return func2(a, c)
  File "cgittest2.py", line 5, in func2
    return a / divisor
ZeroDivisionError: division by zero

You might also consider using the built-in Python module, cgitb, to get some really good, nicely formatted exception information including local variable values, source code context, function parameters etc..

For instance for this code…

import cgitb
cgitb.enable(format='text')

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    return func2(a, c)

func1(1, 5)

we get this exception output…

ZeroDivisionError
Python 3.4.2: C:\tools\python\python.exe
Tue Sep 22 15:29:33 2015

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 c:\TEMP\cgittest2.py in <module>()
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
func1 = <function func1>

 c:\TEMP\cgittest2.py in func1(a=1, b=5)
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
global func2 = <function func2>
a = 1
c = 0

 c:\TEMP\cgittest2.py in func2(a=1, divisor=0)
    3
    4 def func2(a, divisor):
    5   return a / divisor
    6
    7 def func1(a, b):
a = 1
divisor = 0
ZeroDivisionError: division by zero
    __cause__ = None
    __class__ = <class 'ZeroDivisionError'>
    __context__ = None
    __delattr__ = <method-wrapper '__delattr__' of ZeroDivisionError object>
    __dict__ = {}
    __dir__ = <built-in method __dir__ of ZeroDivisionError object>
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __eq__ = <method-wrapper '__eq__' of ZeroDivisionError object>
    __format__ = <built-in method __format__ of ZeroDivisionError object>
    __ge__ = <method-wrapper '__ge__' of ZeroDivisionError object>
    __getattribute__ = <method-wrapper '__getattribute__' of ZeroDivisionError object>
    __gt__ = <method-wrapper '__gt__' of ZeroDivisionError object>
    __hash__ = <method-wrapper '__hash__' of ZeroDivisionError object>
    __init__ = <method-wrapper '__init__' of ZeroDivisionError object>
    __le__ = <method-wrapper '__le__' of ZeroDivisionError object>
    __lt__ = <method-wrapper '__lt__' of ZeroDivisionError object>
    __ne__ = <method-wrapper '__ne__' of ZeroDivisionError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of ZeroDivisionError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of ZeroDivisionError object>
    __repr__ = <method-wrapper '__repr__' of ZeroDivisionError object>
    __setattr__ = <method-wrapper '__setattr__' of ZeroDivisionError object>
    __setstate__ = <built-in method __setstate__ of ZeroDivisionError object>
    __sizeof__ = <built-in method __sizeof__ of ZeroDivisionError object>
    __str__ = <method-wrapper '__str__' of ZeroDivisionError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __suppress_context__ = False
    __traceback__ = <traceback object>
    args = ('division by zero',)
    with_traceback = <built-in method with_traceback of ZeroDivisionError object>

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgittest2.py", line 11, in <module>
    func1(1, 5)
  File "cgittest2.py", line 9, in func1
    return func2(a, c)
  File "cgittest2.py", line 5, in func2
    return a / divisor
ZeroDivisionError: division by zero

回答 6

如果您希望在不处理异常的情况下获得相同的信息,则可以执行以下操作。这样做import traceback,然后:

try:
    ...
except Exception as e:
    print(traceback.print_tb(e.__traceback__))

我正在使用Python 3.7。

If you would like to get the same information given when an exception isn’t handled you can do something like this. Do import traceback and then:

try:
    ...
except Exception as e:
    print(traceback.print_tb(e.__traceback__))

I’m using Python 3.7.


回答 7

对于Python 3.5+

因此,您可以从您的异常以及其他任何异常中获取stacktrace。使用traceback.TracebackException它(只需更换ex您的除外):

print("".join(traceback.TracebackException.from_exception(ex).format())

一个扩展的示例和其他功能可以做到这一点:

import traceback

try:
    1/0
except Exception as ex:
    print("".join(traceback.TracebackException.from_exception(ex).format()) == traceback.format_exc() == "".join(traceback.format_exception(type(ex), ex, ex.__traceback__))) # This is True !!
    print("".join(traceback.TracebackException.from_exception(ex).format()))

输出将是这样的:

True
Traceback (most recent call last):
  File "untidsfsdfsdftled.py", line 29, in <module>
    1/0
ZeroDivisionError: division by zero

For Python 3.5+:

So, you can get the stacktrace from your exception as from any other exception. Use traceback.TracebackException for it (just replace ex with your exception):

print("".join(traceback.TracebackException.from_exception(ex).format())

An extended example and other features to do this:

import traceback

try:
    1/0
except Exception as ex:
    print("".join(traceback.TracebackException.from_exception(ex).format()) == traceback.format_exc() == "".join(traceback.format_exception(type(ex), ex, ex.__traceback__))) # This is True !!
    print("".join(traceback.TracebackException.from_exception(ex).format()))

The output will be something like this:

True
Traceback (most recent call last):
  File "untidsfsdfsdftled.py", line 29, in <module>
    1/0
ZeroDivisionError: division by zero

回答 8

我的2美分:

import sys, traceback
try: 
  ...
except Exception, e:
  T, V, TB = sys.exc_info()
  print ''.join(traceback.format_exception(T,V,TB))

my 2-cents:

import sys, traceback
try: 
  ...
except Exception, e:
  T, V, TB = sys.exc_info()
  print ''.join(traceback.format_exception(T,V,TB))

回答 9

如果您的目标是使异常和stacktrace消息看起来完全像python引发错误时一样,则以下内容在python 2 + 3中均适用:

import sys, traceback


def format_stacktrace():
    parts = ["Traceback (most recent call last):\n"]
    parts.extend(traceback.format_stack(limit=25)[:-2])
    parts.extend(traceback.format_exception(*sys.exc_info())[1:])
    return "".join(parts)

# EXAMPLE BELOW...

def a():
    b()


def b():
    c()


def c():
    d()


def d():
    assert False, "Noooh don't do it."


print("THIS IS THE FORMATTED STRING")
print("============================\n")

try:
    a()
except:
    stacktrace = format_stacktrace()
    print(stacktrace)

print("THIS IS HOW PYTHON DOES IT")
print("==========================\n")
a()

它通过format_stacktrace()从堆栈中删除最后一个调用并加入其余调用来工作。运行时,以上示例给出以下输出:

THIS IS THE FORMATTED STRING
============================

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

THIS IS HOW PYTHON DOES IT
==========================

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

If your goal is to make the exception and stacktrace message look exactly like when python throws an error, the following works in both python 2+3:

import sys, traceback


def format_stacktrace():
    parts = ["Traceback (most recent call last):\n"]
    parts.extend(traceback.format_stack(limit=25)[:-2])
    parts.extend(traceback.format_exception(*sys.exc_info())[1:])
    return "".join(parts)

# EXAMPLE BELOW...

def a():
    b()


def b():
    c()


def c():
    d()


def d():
    assert False, "Noooh don't do it."


print("THIS IS THE FORMATTED STRING")
print("============================\n")

try:
    a()
except:
    stacktrace = format_stacktrace()
    print(stacktrace)

print("THIS IS HOW PYTHON DOES IT")
print("==========================\n")
a()

It works by removing the last format_stacktrace() call from the stack and joining the rest. When run, the example above gives the following output:

THIS IS THE FORMATTED STRING
============================

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

THIS IS HOW PYTHON DOES IT
==========================

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

回答 10

我定义了以下帮助程序类:

import traceback
class TracedExeptions(object):
    def __init__(self):
        pass
    def __enter__(self):
        pass

    def __exit__(self, etype, value, tb):
      if value :
        if not hasattr(value, 'traceString'):
          value.traceString = "\n".join(traceback.format_exception(etype, value, tb))
        return False
      return True

我以后可以这样使用:

with TracedExeptions():
  #some-code-which-might-throw-any-exception

以后可以像这样消费它:

def log_err(ex):
  if hasattr(ex, 'traceString'):
    print("ERROR:{}".format(ex.traceString));
  else:
    print("ERROR:{}".format(ex));

(背景:我很沮丧,因为将Promises与s一起使用Exception,不幸的是将一个地方引发的异常传递给另一个地方的on_rejected处理程序,因此很难从原始位置进行回溯)

I defined following helper class:

import traceback
class TracedExeptions(object):
    def __init__(self):
        pass
    def __enter__(self):
        pass

    def __exit__(self, etype, value, tb):
      if value :
        if not hasattr(value, 'traceString'):
          value.traceString = "\n".join(traceback.format_exception(etype, value, tb))
        return False
      return True

Which I can later use like this:

with TracedExeptions():
  #some-code-which-might-throw-any-exception

And later can consume it like this:

def log_err(ex):
  if hasattr(ex, 'traceString'):
    print("ERROR:{}".format(ex.traceString));
  else:
    print("ERROR:{}".format(ex));

(Background: I was frustraded because of using Promises together with Exceptions, which unfortunately passes exceptions raised in one place to a on_rejected handler in another place, and thus it is difficult to get the traceback from original location)