捕获通用情况下的错误参数异常

6 投票
8 回答
2548 浏览
提问于 2025-04-17 02:47

我想捕捉一个异常,但只在它来自下一级逻辑的时候。

我的目的是处理因为调用函数时传入错误参数数量而导致的错误,而不想掩盖函数实现中产生的错误。

我该如何实现下面的 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 个回答

2

唉,真不太行。你最好的办法是查看返回的错误对象,看看里面有没有提到myfunc和参数的数量。

所以你可以这样做:

except TypeError, err:
    if err.has_some_property or 'myfunc' in str(err):
        fixit()
    raise
4

我不太喜欢这样做魔法。我觉得你可能有一个设计上的问题。

--原始的回答和代码因为不够具体的问题而被删除--

编辑:在理解了具体问题后:

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,它们就会按预期工作。

7

你可以这样做:

    try:
        myfunc()
    except IndexError:
        trace = sys.exc_info()[2]
        if trace.tb_next.tb_next is None:
            pass
        else:
            raise

虽然这样做看起来有点丑,而且似乎违反了封装的原则。

从风格上看,想要捕捉传入了太多参数的情况似乎有点奇怪。我觉得你可能需要更全面地考虑一下你正在做的事情,这样可能会解决问题。不过没有更多的细节,我不能确定。

编辑

一种可能的方法是检查你调用的函数是否有参数 *args,**kwargs。如果有,就假设它是一个装饰器,然后调整上面的代码,检查异常是否是更深一层的问题。如果不是,就像上面那样检查。

不过,我还是觉得你需要重新考虑一下你的解决方案。

撰写回答