Python 在捕获异常后如何返回到 try 块?
我有一些装饰器(Decorators)用于跟踪某个递归函数。我想知道如何返回到try块。我尝试过使用while循环,但对我来说不管用,因为我的函数是递归的。有没有人能给我个主意,怎么处理这个问题?
问题是,当函数change_t抛出异常时,我想继续执行我的try块。
这是我的装饰器和函数:
正确的结果:
,- change_t([9, 7, 5], 44)
| ,- change_t([9, 7, 5], 35)
| | ,- change_t([9, 7, 5], 26)
| | | ,- change_t([9, 7, 5], 17)
| | | | ,- change_t([9, 7, 5], 8)
| | | | | ,- change_t([7, 5], 8)
| | | | | | ,- change_t([7, 5], 1)
| | | | | | | ,- change_t([5], 1)
| | | | | | | | ,- change_t([], 1)
| | | | | | ,- change_t([5], 8)
| | | | | | | ,- change_t([5], 3)
| | | | | | | | ,- change_t([], 3)
| | | | | | | ,- change_t([], 8)
| | | | ,- change_t([7, 5], 17)
| | | | | ,- change_t([7, 5], 10)
| | | | | | ,- change_t([7, 5], 3)
| | | | | | | ,- change_t([5], 3)
| | | | | | | | ,- change_t([], 3)
| | | | | | ,- change_t([5], 10)
| | | | | | | ,- change_t([5], 5)
| | | | | | | | ,- change_t([5], 0)
| | | | | | | | `- []
| | | | | | | `- [5]
| | | | | | `- [5, 5]
| | | | | `- [5, 5]
| | | | `- [7, 5, 5]
| | | `- [7, 5, 5]
| | `- [9, 7, 5, 5]
| `- [9, 9, 7, 5, 5]
`- [9, 9, 9, 7, 5, 5]
这是我得到的结果:它在我预期会出现异常的地方停止了。
change_t([9, 7, 5], 44)
,- change_t ([9, 7, 5], 44)
| ,- change_t ([9, 7, 5], 35)
| | ,- change_t ([9, 7, 5], 26)
| | | ,- change_t ([9, 7, 5], 17)
| | | | ,- change_t ([9, 7, 5], 8)
| | | | | ,- change_t ([7, 5], 8)
| | | | | | ,- change_t ([7, 5], 1)
| | | | | | | ,- change_t ([5], 1)
| | | | | | | | ,- change_t ([], 1)
| | | | | | `- 1
| | | | | `- 1
| | | | `- 1
| `- 8
`- 8
`- 17
`- 26
`- 35
`- 44
44
2 个回答
0
当程序出现错误(我们称之为异常)时,控制权就会从出错的代码中转移出去。这个控制权会交给第一个能够处理这个错误的地方,或者直接交给程序的主循环。
看看你的代码:
if a==0:
return []
elif len(l)==0:
raise ChangeException()
elif l[0]>a:
return change_t(l[1:],a)
假设第三个条件成立,也就是 l[0]>a
,而且列表 l 的长度是 1,这时会发生什么呢?在下一次调用时,也就是 return change_t(l[1:],a)
,程序会抛出一个异常,而这个异常除了主循环之外没有人能处理:这就是你代码出错的原因。你需要把这个第三个条件放在一个尝试捕获的结构里,这样可以根据你想要实现的目标来处理异常。
2
正如我在评论中提到的,你需要重新抛出异常,这样原来的函数才能捕捉到这个异常并继续执行。你只需要在异常处理的部分减少一个缩进,这样就不会缩得太靠左了:
class traced(object):
indent =0
def __init__(self,f):
self.__name__=f.__name__
self.indent=0
self.f=f
def __call__(self,*args,**kwargs):
string=""
if kwargs:
l=[]
for (key, value) in kwargs.items():
l.append(str(key) + "=" + str(value))
a=', '.join(l)
string = '('+a+')'
else:
l=[]
for value in args:
l.append(str(value))
a=', '.join(l)
string = '('+a+')'
print('| ' * traced.indent + ',- '+ self.__name__+' '+string)
try:
traced.indent+=1
value = self.f(*args,**kwargs)
except Exception:
traced.indent-=1 # <-- only decrement by one
raise # <-- reraise the exception so the original function can catch it
traced.indent-=1
print('| '* traced.indent + "`- "+ repr(value))
return value
这样就可以正常工作了:
>>> change_t([9, 7, 5], 44)
,- change_t ([9, 7, 5], 44)
| ,- change_t ([9, 7, 5], 35)
| | ,- change_t ([9, 7, 5], 26)
| | | ,- change_t ([9, 7, 5], 17)
| | | | ,- change_t ([9, 7, 5], 8)
| | | | | ,- change_t ([7, 5], 8)
| | | | | | ,- change_t ([7, 5], 1)
| | | | | | | ,- change_t ([5], 1)
| | | | | | | | ,- change_t ([], 1)
| | | | | | ,- change_t ([5], 8)
| | | | | | | ,- change_t ([5], 3)
| | | | | | | | ,- change_t ([], 3)
| | | | | | | ,- change_t ([], 8)
| | | | ,- change_t ([7, 5], 17)
| | | | | ,- change_t ([7, 5], 10)
| | | | | | ,- change_t ([7, 5], 3)
| | | | | | | ,- change_t ([5], 3)
| | | | | | | | ,- change_t ([], 3)
| | | | | | ,- change_t ([5], 10)
| | | | | | | ,- change_t ([5], 5)
| | | | | | | | ,- change_t ([5], 0)
| | | | | | | | `- []
| | | | | | | `- [5]
| | | | | | `- [5, 5]
| | | | | `- [5, 5]
| | | | `- [7, 5, 5]
| | | `- [7, 5, 5]
| | `- [9, 7, 5, 5]
| `- [9, 9, 7, 5, 5]
`- [9, 9, 9, 7, 5, 5]
[9, 9, 9, 7, 5, 5]
最后,我会稍微整理一下装饰器,让它看起来更简洁,也更清楚你在做什么:
class traced(object):
indent = 0
def __init__(self, f):
self.__name__ = f.__name__
self.f = f
def __call__(self, *args, **kwargs):
if kwargs:
l = [str(key) + '=' + str(value) for key, value in kwargs.items()]
else:
l = list(map(str, args))
print('| ' * traced.indent + ',- {0} ({1})'.format(self.__name__, ', '.join(l)))
try:
traced.indent += 1
value = self.f(*args,**kwargs)
finally:
traced.indent -= 1
print('| ' * traced.indent + '`- ' + repr(value))
return value
在这里,我把所有的参数聚合简化成了列表推导式。同时,我使用了字符串格式化,让格式看起来更清晰。这样你也可以把原本需要加在列表内容周围的括号合并起来(顺便说一下,你在两个例子中都这样做了)。而且因为你重新抛出了异常,并没有真正去查看它,所以我们一开始并不需要捕捉这个异常,只需要确保在最终块中调整好缩进就可以了。
其实,为什么要检查是可变参数还是关键字参数呢?直接接受两者就行了:
l = list(map(str, args))
l.extend([str(key) + '=' + str(value) for key, value in kwargs.items()])