在一般情况下,捕获错误参数异常

时间:2011-09-21 18:11:46

标签: python exception try-catch decorator

我想捕获一个异常,但前提是它来自下一级逻辑。

目的是处理由使用错误数量的参数调用函数的行为引起的错误,而不会掩盖函数实现生成的错误。

如何实施以下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的argspec。异常检查似乎也不可靠,因为myfunc.__name__对于大多数装饰器而言只是“包装”,而不管核心功能的名称。

如果函数可能有也可能没有装饰器,是否有任何好的解决方案?

8 个答案:

答案 0 :(得分:7)

你可以这样做:

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

虽然它有点难看并且似乎违反了封装。

在风格上,想要抓住过多的论点似乎很奇怪。我怀疑对你正在做的事情进行更普遍的反思可能会解决问题。但没有更多细节,我无法确定。

修改

可能的方法:检查您正在调用的函数是否具有参数*args,**kwargs。如果是这样,假设它是一个装饰器并调整上面的代码以检查异常是否是另一个层。如果不是,请检查如上。

不过,我认为您需要重新考虑您的解决方案。

答案 1 :(得分: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()

所有装饰者,或者至少是那些你想要透明的装饰者 通过上面的代码调用,需要设置&#39; effective_argspec&#39;在返回的callable上。 非常明确,没有魔力。为此,您可以使用适当的代码装饰装饰器......

编辑:更多代码,透明装饰器的装饰器。

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,它们的工作方式与预期完全相同。

答案 2 :(得分:2)

不幸的是,不是真的。最好的办法是内省返回的错误对象,看看是否提到了myfunc和参数个数。

所以你会做类似的事情:

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

答案 3 :(得分:2)

你可以通过做

这样的事情来做到这一点
>>> def f(x,y,z):
    print (f(0))


>>> try:
    f(0)
except TypeError as e:
    print (e.__traceback__.tb_next is None)


True
>>> try:
    f(0,1,2)
except TypeError as e:
    print (e.__traceback__.tb_next is None)


False

但更好的方法是计算函数的args数量并与预期的args数量进行比较

len(inspect.getargspec(f).args) != len (args)

答案 4 :(得分:1)

您可以检索回溯并查看其长度。尝试:

import traceback as tb
import sys

def a():
    1/0

def b():
    a()

def c():
    b()

try:
    a()
except:
    print len(tb.extract_tb(sys.exc_traceback))

try:
    b()
except:
    print len(tb.extract_tb(sys.exc_traceback))

try:
    c()
except:
    print len(tb.extract_tb(sys.exc_traceback))

打印

2
3
4

答案 5 :(得分:1)

写得好的包装器将保留它们包装的函数的函数名,签名等;但是,如果你必须支持那些没有的包装器,或者如果你想要在包装器中捕获错误(不仅仅是最终包装的函数),那么就没有通用的解决方案可行。

答案 6 :(得分:1)

我知道这是一个老帖子,但我偶然发现了这个问题,后来得到了更好的答案。这个答案取决于python 3中的一个新功能Signature objects

使用该功能,您可以写:

sig = inspect.signature(myfunc)
try:
    sig.bind(*args)
except TypeError:
    return fixit()
else:
    f(*args)

答案 7 :(得分:0)

对我来说,你正在尝试做的事情正是异常应该解决的问题,即异常将被调用到调用堆栈的某个地方,这样就不需要向上传播错误了。

相反,它听起来像是在尝试对C(非异常处理)方式进行错误处理,其中函数的返回值指示无错误(通常为0)或错误(非0值)。所以,我会尝试编写函数来返回一个值,并让调用者检查返回值。