捕获通用情况下的错误参数异常
我想捕捉一个异常,但只在它来自下一级逻辑的时候。
我的目的是处理因为调用函数时传入错误参数数量而导致的错误,而不想掩盖函数实现中产生的错误。
我该如何实现下面的 wrong_arguments
函数呢?
举个例子:
try:
return myfunc(*args)
except TypeError, error:
#possibly wrong number of arguments
#we know how to proceed if the error occurred when calling myfunc(),
#but we shouldn't interfere with errors in the implementation of myfunc
if wrong_arguments(error, myfunc):
return fixit()
else:
raise
补充说明:
有几种解决方案在简单情况下效果不错,但目前的答案在实际使用中,尤其是涉及装饰器的函数时,都不太管用。
想象一下,上面提到的 myfunc
可能有以下几种值:
def decorator(func):
"The most trivial (and common) decorator"
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
def myfunc1(a, b, c='ok'):
return (a, b, c)
myfunc2 = decorator(myfunc1)
myfunc3 = decorator(myfunc2)
即使是小心翼翼的“先看后跳”方法(检查函数参数规范)在这里也失效了,因为大多数装饰器的参数规范都是 *args, **kwargs
,无论被装饰的函数是什么。检查异常似乎也不可靠,因为大多数装饰器的 myfunc.__name__
只是显示为“wrapper”,而不管核心函数的名字是什么。
如果函数可能有也可能没有装饰器,有什么好的解决方案吗?
8 个回答
唉,真不太行。你最好的办法是查看返回的错误对象,看看里面有没有提到myfunc和参数的数量。
所以你可以这样做:
except TypeError, err:
if err.has_some_property or 'myfunc' in str(err):
fixit()
raise
我不太喜欢这样做魔法。我觉得你可能有一个设计上的问题。
--原始的回答和代码因为不够具体的问题而被删除--
编辑:在理解了具体问题后:
from inspect import getargspec
def can_call_effectively(f, args):
(fargs, varargs, _kw, df) = getattr(myfunc, 'effective_argspec', \
getargspec(myfunc))
fargslen = len(fargs)
argslen = len(args)
minargslen = fargslen - len(df)
return (varargs and argslen >= minargslen) or minargslen <= argslen <= fargslen
if can_call_effectively(myfunc, args)
myfunc(*args)
else:
fixit()
你所有的装饰器,或者至少是那些你希望在上面的代码中透明调用的装饰器,都需要在返回的可调用对象上设置'effective_argspec'。这个要求很明确,没有什么魔法。为了实现这个,你可以用适当的代码来装饰你的装饰器……
编辑:更多代码,透明装饰器的装饰器。
def transparent_decorator(decorator):
def wrapper(f):
wrapped = decorator(f)
wrapped.__doc__ = f.__doc__
wrapped.effective_argspec = getattr(f, 'effective_argspec', getargspec(f))
return wrapped
return wrapper
在你的装饰器上使用这个:
@transparent_decorator
def decorator(func):
"The most trivial (and common) decorator"
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper # line missing in example above
现在如果你像上面那样创建myfunc1到myfunc3,它们就会按预期工作。
你可以这样做:
try:
myfunc()
except IndexError:
trace = sys.exc_info()[2]
if trace.tb_next.tb_next is None:
pass
else:
raise
虽然这样做看起来有点丑,而且似乎违反了封装的原则。
从风格上看,想要捕捉传入了太多参数的情况似乎有点奇怪。我觉得你可能需要更全面地考虑一下你正在做的事情,这样可能会解决问题。不过没有更多的细节,我不能确定。
编辑
一种可能的方法是检查你调用的函数是否有参数 *args,**kwargs
。如果有,就假设它是一个装饰器,然后调整上面的代码,检查异常是否是更深一层的问题。如果不是,就像上面那样检查。
不过,我还是觉得你需要重新考虑一下你的解决方案。