如何在Python中捕获异常并获取异常引用,而无需知道类型?

27 投票
3 回答
14695 浏览
提问于 2025-04-15 17:54

我在想,怎么才能捕捉到任何抛出的对象(也就是说,那些不继承Exception的类型),并且还能获取到它的引用。

我在使用Jython的时候遇到了这个问题。当调用一个Java方法时,如果这个方法抛出了一个异常,它不会继承Python的Exception类,所以像下面这样的代码是捕捉不到的:

try:
    # some call to a java lib that raises an exception here
except Exception, e:
    # will never be entered

我可以这样做,但这样我就无法访问被抛出的异常对象了。

try:
    # some call to a java lib that raises an exception here
except:
    # will enter here, but there's no reference to the exception that was raised

我可以通过导入Java的异常类型来解决这个问题,并明确捕捉它,但这样会让编写通用的异常处理包装器或装饰器变得困难甚至不可能。

有没有办法在except块中捕捉到某个任意的异常,并且还能获取到它的引用呢?

我想说明一下,我希望我正在制作的异常处理装饰器能够在Python项目中使用,而不仅仅是Jython项目。我想避免导入java.lang.Exception,因为那样就只能在Jython中使用了。例如,我觉得我可以这样做(但我还没试过),不过如果能避免的话,我希望还是不要这样。

try:
    # some function that may be running jython and may raise a java exception
except (Exception, java.lang.Exception), e:
    # I imagine this would work, but it makes the code jython-only

3 个回答

5

对感兴趣的人来说... 我花了一些时间测试了一些东西,因为我想知道如何正确获取堆栈跟踪,无论是抛出Python异常(实际上是BaseException,这是基类)还是java.lang.Throwable(Java中异常、错误等的基类)。这段代码展示了如何正确捕获所有行号引用。

import sys
import traceback
import java

print "hello world"

def custom_hook( type, exc, tb ):
  if isinstance( sys.exc_info()[ 1 ], java.lang.Throwable ):
    sys.stderr.write( "AS JAVA:\n" )
    sys.exc_info()[ 1 ].printStackTrace() # java part
  else:
    sys.stderr.write( "NO JAVA TRACE:\n" )
  sys.stderr.write( "AS PYTHON:\n" )
  traceback.print_exc()

# useful for custom exception handling!
sys.excepthook = custom_hook  

def handle_exc():
# generate either a java.lang.Throwable (uncomment the next line and comment out "a = 16 / 0"
#  java.lang.String( None )
# OR... a python-style BaseException:
  a = 16 / 0 

class Task( java.lang.Runnable ):
  def run( self ):
    # NB the problem with all this stuff is that the Java stack trace shows
    # a java.lang.Throwable occurring at the last line of this code block...
#    print "lots of stuff first"
#    print "lots 2"
#    handle_exc()
#    print "lots 3"
#    print "lots of stuff after"

    try:
      print "lots of stuff first"
      print "lots 2"
      handle_exc()
      print "lots 3"
      print "lots of stuff after"
    # NB do not catch both (Python) BaseException and java.lang.Throwable...   
#    except ( BaseException, java.lang.Throwable ), e:
    # the above means that the line no. in handle_exc is not shown when a BaseException  
    # is thrown...
    except java.lang.Throwable, t:
      tb = sys.exc_info()[ 2 ] 
      sys.stderr.write( "java.lang.Throwable thrown at: %s\n" % tb.tb_lineno )
      raise t

java.awt.EventQueue.invokeAndWait( Task() )

在这之后,可能会考虑写一个装饰器,放在def run(self)和类似的方法前面,这样就不需要每次都写这个捕获Throwable的try-except块了... 具体来说:

def throw_trap( function ):
  def wrapper(*args, **kvargs):
    try:
      return function( *args, **kvargs )
    except  java.lang.Throwable, t:
      tb = sys.exc_info()[ 2 ]
      while( tb ): 
        sys.stderr.write( "thrown at: %s\n" % tb.tb_lineno )
        tb = tb.tb_next
      raise t
  return wrapper



def handle_exc():
  java.lang.String( None )
#  a = 16 / 0 


class Task( java.lang.Runnable ):
  @throw_trap
  def run( self ):
    print "lots of stuff first"
    print "lots 2"
    handle_exc()
    print "lots 3"
    print "lots of stuff after"

java.awt.EventQueue.invokeAndWait( Task() )
9

顺便说一下,我发现如果你在你的Jython脚本中添加这个导入:

from java.lang import Exception

然后只用普通的Python异常处理方式:

except Exception, e:

它会同时捕捉到 Python 的异常和 Java 的异常

37

你可以通过 sys 这个模块来引用异常。 sys.exc_info 是一个包含三部分的元组:异常的类型、实例和追踪信息。

import sys

try:
    # some call to a java lib that raises an exception here
except:
    instance = sys.exc_info()[1]

撰写回答