我想捕获一个异常,但前提是它来自下一级逻辑。
目的是处理由使用错误数量的参数调用函数的行为引起的错误,而不会掩盖函数实现生成的错误。
如何实施以下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__
对于大多数装饰器而言只是“包装”,而不管核心功能的名称。
如果函数可能有也可能没有装饰器,是否有任何好的解决方案?
答案 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)
所以你会做类似的事情:
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值)。所以,我会尝试编写函数来返回一个值,并让调用者检查返回值。