唯一的方法是保持__call__方法的不同版本,因为exec本身就创建了一个堆栈框架!下面,在Python 2中再次显示了该框架:class SneakyDecorator:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
exec("raise v, None, tb.tb_next")
结果:
^{pr2}$
所以使用:def __call__(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
if sys.version_info[0] <= 2:
exec("raise v, None, tb.tb_next")
else:
raise v.with_traceback(tb.tb_next)
不会真正起作用。在
对于Python 2,以下功能与您的两个独立类具有完全相同的功能:if sys.version_info[0] <= 2:
_call = '''\
def _call(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
raise v, None, tb.tb_next
'''
exec(_call)
else:
def _call(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
raise v.with_traceback(tb.tb_next)
class SneakyDecorator:
def __init__(self, f):
self.f = f
__call__ = _call
# niceties, patch up name and qualified name. Optional.
__call__.__name__ = '__call__'
__call__.__qualname__ = 'SneakyDecorator.__call__'
但是,请注意,对于python3,Exception.with_traceback()方法可能允许您在删除当前帧的情况下附加一个新的回溯,但是Python会在您重新引发异常的那一刻将它重新附加到!在