问题:python:我怎么知道发生了什么类型的异常?
我有一个主程序调用的函数:
try:
someFunction()
except:
print "exception happened!"
但是在执行函数的中间会引发异常,因此它跳到了该except
部分。
我如何才能确切地看到someFunction()
导致异常发生的原因?
I have a function called by the main program:
try:
someFunction()
except:
print "exception happened!"
but in the middle of the execution of the function it raises exception, so it jumps to the except
part.
How can I see exactly what happened in the someFunction()
that caused the exception to happen?
回答 0
其他答案都指出,您不应捕获通用异常,但似乎没有人想告诉您原因,这对于理解何时可以打破“规则”至关重要。这是一个解释。基本上是这样,所以您不会隐藏:
因此,只要您不做任何事情,就可以捕获通用异常。例如,您可以通过另一种方式向用户提供有关异常的信息,例如:
- 在GUI中将异常显示为对话框
- 将异常从工作线程或进程转移到多线程或多处理应用程序中的控制线程或进程
那么如何捕获通用异常呢?有几种方法。如果只需要异常对象,请按照以下步骤操作:
try:
someFunction()
except Exception as ex:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
print message
请确保 message
被带到用户的注意力在一个难以错过的方式!如上所示,如果将消息掩埋在许多其他消息中,则可能不够用。未能引起用户的注意无异于吞没所有exceptions,如果您有任何印象,在阅读完本页上的答案后应该会消失,这不是一件好事。在except块末尾添加一个raise
语句将通过透明地重新引发捕获的异常来解决该问题。
上面的代码和使用except:
不带任何参数的代码之间的区别是双重的:
- 裸机
except:
不会给您检查异常对象
- 上面的代码通常不会捕获这些异常
SystemExit
,KeyboardInterrupt
并且GeneratorExit
通常是您想要的。请参阅异常层次结构。
如果您还希望在不捕获异常的情况下获得相同的堆栈跟踪,则可以这样获取(仍在except子句内):
import traceback
print traceback.format_exc()
如果您使用logging
模块,则可以将异常打印到日志(以及消息)中,如下所示:
import logging
log = logging.getLogger()
log.exception("Message for you, sir!")
如果您想更深入地研究堆栈,查看变量等,请使用except块内的模块post_mortem
功能pdb
:
import pdb
pdb.post_mortem()
我发现在寻找错误时,这最后一种方法是无价的。
The other answers all point out that you should not catch generic exceptions, but no one seems to want to tell you why, which is essential to understanding when you can break the “rule”. Here is an explanation. Basically, it’s so that you don’t hide:
So as long as you take care to do none of those things, it’s OK to catch the generic exception. For instance, you could provide information about the exception to the user another way, like:
- Present exceptions as dialogs in a GUI
- Transfer exceptions from a worker thread or process to the controlling thread or process in a multithreading or multiprocessing application
So how to catch the generic exception? There are several ways. If you just want the exception object, do it like this:
try:
someFunction()
except Exception as ex:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
print message
Make sure message
is brought to the attention of the user in a hard-to-miss way! Printing it, as shown above, may not be enough if the message is buried in lots of other messages. Failing to get the users attention is tantamount to swallowing all exceptions, and if there’s one impression you should have come away with after reading the answers on this page, it’s that this is not a good thing. Ending the except block with a raise
statement will remedy the problem by transparently reraising the exception that was caught.
The difference between the above and using just except:
without any argument is twofold:
- A bare
except:
doesn’t give you the exception object to inspect
- The exceptions
SystemExit
, KeyboardInterrupt
and GeneratorExit
aren’t caught by the above code, which is generally what you want. See the exception hierarchy.
If you also want the same stacktrace you get if you do not catch the exception, you can get that like this (still inside the except clause):
import traceback
print traceback.format_exc()
If you use the logging
module, you can print the exception to the log (along with a message) like this:
import logging
log = logging.getLogger()
log.exception("Message for you, sir!")
If you want to dig deeper and examine the stack, look at variables etc., use the post_mortem
function of the pdb
module inside the except block:
import pdb
pdb.post_mortem()
I’ve found this last method to be invaluable when hunting down bugs.
回答 1
获取异常对象所属的类的名称:
e.__class__.__name__
并且使用print_exc()函数还将打印堆栈跟踪,这对于任何错误消息都是必不可少的信息。
像这样:
from traceback import print_exc
class CustomException(Exception): pass
try:
raise CustomException("hi")
except Exception, e:
print 'type is:', e.__class__.__name__
print_exc()
# print "exception happened!"
您将获得如下输出:
type is: CustomException
Traceback (most recent call last):
File "exc.py", line 7, in <module>
raise CustomException("hi")
CustomException: hi
在打印和分析之后,代码可以决定不处理异常,而只是执行raise
:
from traceback import print_exc
class CustomException(Exception): pass
def calculate():
raise CustomException("hi")
try:
calculate()
except Exception, e:
if e.__class__ == CustomException:
print 'special case of', e.__class__.__name__, 'not interfering'
raise
print "handling exception"
输出:
special case of CustomException not interfering
解释器输出异常:
Traceback (most recent call last):
File "test.py", line 9, in <module>
calculate()
File "test.py", line 6, in calculate
raise CustomException("hi")
__main__.CustomException: hi
经过raise
最初的异常继续进一步传播调用堆栈。(当心可能的陷阱)如果引发新的异常,它将产生新的(较短的)堆栈跟踪。
from traceback import print_exc
class CustomException(Exception): pass
def calculate():
raise CustomException("hi")
try:
calculate()
except Exception, e:
if e.__class__ == CustomException:
print 'special case of', e.__class__.__name__, 'not interfering'
#raise CustomException(e.message)
raise e
print "handling exception"
输出:
special case of CustomException not interfering
Traceback (most recent call last):
File "test.py", line 13, in <module>
raise CustomException(e.message)
__main__.CustomException: hi
请注意,traceback如何不包括calculate()
来自9
作为原始异常源的line函数e
。
Get the name of the class that exception object belongs:
e.__class__.__name__
and using print_exc() function will also print stack trace which is essential info for any error message.
Like this:
from traceback import print_exc
class CustomException(Exception): pass
try:
raise CustomException("hi")
except Exception, e:
print 'type is:', e.__class__.__name__
print_exc()
# print "exception happened!"
You will get output like this:
type is: CustomException
Traceback (most recent call last):
File "exc.py", line 7, in <module>
raise CustomException("hi")
CustomException: hi
And after print and analysis, the code can decide not to handle exception and just execute raise
:
from traceback import print_exc
class CustomException(Exception): pass
def calculate():
raise CustomException("hi")
try:
calculate()
except Exception, e:
if e.__class__ == CustomException:
print 'special case of', e.__class__.__name__, 'not interfering'
raise
print "handling exception"
Output:
special case of CustomException not interfering
And interpreter prints exception:
Traceback (most recent call last):
File "test.py", line 9, in <module>
calculate()
File "test.py", line 6, in calculate
raise CustomException("hi")
__main__.CustomException: hi
After raise
original exception continues to propagate further up the call stack. (Beware of possible pitfall) If you raise new exception it caries new (shorter) stack trace.
from traceback import print_exc
class CustomException(Exception): pass
def calculate():
raise CustomException("hi")
try:
calculate()
except Exception, e:
if e.__class__ == CustomException:
print 'special case of', e.__class__.__name__, 'not interfering'
#raise CustomException(e.message)
raise e
print "handling exception"
Output:
special case of CustomException not interfering
Traceback (most recent call last):
File "test.py", line 13, in <module>
raise CustomException(e.message)
__main__.CustomException: hi
Notice how traceback does not include calculate()
function from line 9
which is the origin of original exception e
.
回答 2
通常,您不应捕获所有可能的异常,try: ... except
因为这过于广泛。只要抓住由于任何原因而可能发生的事件。如果确实需要,例如,如果您想在调试时查找有关某个问题的更多信息,则应该这样做
try:
...
except Exception as ex:
print ex # do whatever you want for debugging.
raise # re-raise exception.
You usually should not catch all possible exceptions with try: ... except
as this is overly broad. Just catch those that are expected to happen for whatever reason. If you really must, for example if you want to find out more about some problem while debugging, you should do
try:
...
except Exception as ex:
print ex # do whatever you want for debugging.
raise # re-raise exception.
回答 3
除非somefunction
是一个非常糟糕的编码遗留函数,否则您不需要所要的内容。
使用multiple except
子句以不同的方式处理不同的异常:
try:
someFunction()
except ValueError:
# do something
except ZeroDivision:
# do something else
要点是,您不应捕获一般异常,而应捕获所需的异常。我确定您不想掩盖意外的错误或错误。
Unless somefunction
is a very bad coded legacy function, you shouldn’t need what you’re asking.
Use multiple except
clause to handle in different ways different exceptions:
try:
someFunction()
except ValueError:
# do something
except ZeroDivision:
# do something else
The main point is that you shouldn’t catch generic exception, but only the ones that you need to. I’m sure that you don’t want to shadow unexpected errors or bugs.
回答 4
大多数答案都指向except (…) as (…):
语法(正确地是这样),但与此同时,没有人愿意谈论房间里的大象,那里的大象是有sys.exc_info()
功能的。从文档的SYS模块(重点煤矿):
此函数返回三个值的元组,它们给出有关当前正在处理的异常的信息。
(…)
如果没有在堆栈上的任何地方处理异常,则返回包含三个None值的元组。否则,返回的值是(类型,值,回溯)。它们的含义是:type获取要处理的异常的类型(BaseException的子类);value获取异常实例(异常类型的实例);traceback获取一个traceback对象(请参见参考手册),该对象将调用堆栈封装在最初发生异常的位置。
我认为sys.exc_info()
可以将其视为原始问题“ 我如何知道发生了哪种异常的最直接答案” 。
Most answers point to except (…) as (…):
syntax (rightly so) but at the same time nobody wants to talk about an elephant in the room, where the elephant is sys.exc_info()
function.
From the documentation of sys module (emphasis mine):
This function returns a tuple of three values that give information
about the exception that is currently being handled.
(…)
If no exception is being handled anywhere on the stack, a tuple
containing three None values is returned. Otherwise, the values
returned are (type, value, traceback). Their meaning is: type gets the
type of the exception being handled (a subclass of BaseException);
value gets the exception instance (an instance of the exception type);
traceback gets a traceback object (see the Reference Manual) which
encapsulates the call stack at the point where the exception
originally occurred.
I think the sys.exc_info()
could be treated as the most direct answer to the original question of How do I know what type of exception occurred?
回答 5
尝试:someFunction()除外,exceptions:
#this is how you get the type
excType = exc.__class__.__name__
#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)
#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))
try:
someFunction()
except Exception, exc:
#this is how you get the type
excType = exc.__class__.__name__
#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)
#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))
回答 6
这些答案非常适合调试,但是对于以编程方式测试异常来说,isinstance(e, SomeException)
它很方便,因为它也可以测试子类SomeException
,因此您可以创建适用于异常层次结构的功能。
These answers are fine for debugging, but for programmatically testing the exception, isinstance(e, SomeException)
can be handy, as it tests for subclasses of SomeException
too, so you can create functionality that applies to hierarchies of exceptions.
回答 7
这是我处理异常的方式。这样做的想法是尝试解决问题,如果可能的话,然后再添加一个更理想的解决方案。不要在生成异常的代码中解决问题,否则该代码会失去对原始算法的跟踪,应将其写入现场。但是,传递解决问题所需的数据,并返回lambda,以防万一您无法在生成它的代码之外解决问题。
path = 'app.p'
def load():
if os.path.exists(path):
try:
with open(path, 'rb') as file:
data = file.read()
inst = pickle.load(data)
except Exception as e:
inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
else:
inst = App()
inst.loadWidgets()
# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
class_name = e.__class__.__name__
print(class_name + ': ' + str(e))
print('\t during: ' + during)
return easy
目前,由于我不想与应用程序的目的相切,所以我没有添加任何复杂的解决方案。但是将来,当我更多地了解可能的解决方案时(由于该应用程序的设计更多),我可以添加一个由索引的解决方案字典during
。
在所示的示例中,一种解决方案可能是查找存储在其他位置的应用程序数据,例如说是否“ app.p”文件被误删除了。
目前,由于编写异常处理程序不是一个聪明的主意(我们尚不知道解决异常的最佳方法,因为应用程序设计会不断发展),因此我们仅返回简单的修复程序,其作用就像我们在运行一样首次使用该应用(在这种情况下)。
Here’s how I’m handling my exceptions. The idea is to do try solving the issue if that’s easy, and later add a more desirable solution if possible. Don’t solve the issue in the code that generates the exception, or that code loses track of the original algorithm, which should be written to-the-point. However, pass what data is needed to solve the issue, and return a lambda just in case you can’t solve the problem outside of the code that generates it.
path = 'app.p'
def load():
if os.path.exists(path):
try:
with open(path, 'rb') as file:
data = file.read()
inst = pickle.load(data)
except Exception as e:
inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
else:
inst = App()
inst.loadWidgets()
# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
class_name = e.__class__.__name__
print(class_name + ': ' + str(e))
print('\t during: ' + during)
return easy
For now, since I don’t want to think tangentially to my app’s purpose, I haven’t added any complicated solutions. But in the future, when I know more about possible solutions (since the app is designed more), I could add in a dictionary of solutions indexed by during
.
In the example shown, one solution might be to look for app data stored somewhere else, say if the ‘app.p’ file got deleted by mistake.
For now, since writing the exception handler is not a smart idea (we don’t know the best ways to solve it yet, because the app design will evolve), we simply return the easy fix which is to act like we’re running the app for the first time (in this case).
回答 8
为了增加Lauritz的答案,我创建了一个用于处理异常的装饰器/包装器,并且包装器记录了发生哪种类型的异常。
class general_function_handler(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
return self.__class__(self.func.__get__(obj, type))
def __call__(self, *args, **kwargs):
try:
retval = self.func(*args, **kwargs)
except Exception, e :
logging.warning('Exception in %s' % self.func)
template = "An exception of type {0} occured. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
logging.exception(message)
sys.exit(1) # exit on all exceptions for now
return retval
这可以在类方法或带有装饰器的独立函数上调用:
@general_function_handler
请参阅我的博客,以获取完整示例:http : //ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/
To add to Lauritz’s answer, I created a decorator/wrapper for exception handling and the wrapper logs which type of exception occurred.
class general_function_handler(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
return self.__class__(self.func.__get__(obj, type))
def __call__(self, *args, **kwargs):
try:
retval = self.func(*args, **kwargs)
except Exception, e :
logging.warning('Exception in %s' % self.func)
template = "An exception of type {0} occured. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
logging.exception(message)
sys.exit(1) # exit on all exceptions for now
return retval
This can be called on a class method or a standalone function with the decorator:
@general_function_handler
See my blog about for the full example: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/
回答 9
您可以按照Lauritz的建议开始:
except Exception as ex:
然后就print ex
这样:
try:
#your try code here
except Exception as ex:
print ex
You can start as Lauritz recommended, with:
except Exception as ex:
and then just to print ex
like so:
try:
#your try code here
except Exception as ex:
print ex
回答 10
可以通过以下方式捕获实际的异常:
try:
i = 1/0
except Exception as e:
print e
您可以从The Python Tutorial了解更多有关异常的信息。
The actual exception can be captured in the following way:
try:
i = 1/0
except Exception as e:
print e
You can learn more about exceptions from The Python Tutorial.
回答 11
您的问题是:“我如何才能确切地看到someFunction()中发生了什么导致异常发生?”
在我看来,您不是在问如何在生产代码中处理无法预料的异常(假设有很多答案),而是如何找出导致开发过程中特定异常的原因。
最简单的方法是使用调试器,该调试器可以在发生未捕获的异常的地方停止(最好不退出),以便您可以检查变量。例如,Eclipse开源IDE中的PyDev可以做到这一点。要在Eclipse中启用它,请打开Debug透视图,Manage Python Exception Breakpoints
在Run
菜单中选择,然后选中Suspend on uncaught exceptions
。
Your question is: “How can I see exactly what happened in the someFunction() that caused the exception to happen?”
It seems to me that you are not asking about how to handle unforeseen exceptions in production code (as many answers assumed), but how to find out what is causing a particular exception during development.
The easiest way is to use a debugger that can stop where the uncaught exception occurs, preferably not exiting, so that you can inspect the variables. For example, PyDev in the Eclipse open source IDE can do that. To enable that in Eclipse, open the Debug perspective, select Manage Python Exception Breakpoints
in the Run
menu, and check Suspend on uncaught exceptions
.
回答 12
仅避免捕获异常,Python打印的回溯将告诉您发生了什么异常。
Just refrain from catching the exception and the traceback that Python prints will tell you what exception occurred.