如何找出缺少哪个Python关键字参数?

如何找出缺少哪个Python关键字参数?,python,inspect,keyword-argument,Python,Inspect,Keyword Argument,当忘记将某些参数传递给函数时,Python会给出唯一有用的消息“myfunction()接受X个参数(Y给定)”。有没有办法找出缺失参数的名称并告诉用户?比如: try: #begin blackbox def f(x,y): return x*y f(x=1) #end blackbox except Exception as e: #figure out the missing keyword argument is called "

当忘记将某些参数传递给函数时,Python会给出唯一有用的消息“myfunction()接受X个参数(Y给定)”。有没有办法找出缺失参数的名称并告诉用户?比如:

try:
    #begin blackbox
    def f(x,y):
        return x*y

    f(x=1)
    #end blackbox
except Exception as e:
    #figure out the missing keyword argument is called "y" and tell the user so
假设异常处理程序不知道begin blackbox和end blackbox之间的代码

编辑:正如下面向我指出的,Python3已经内置了这个功能。那么,让我扩展一下这个问题,在Python2.x中是否有一种(可能是丑陋的和黑客的)方法来实现这一点

except TypeError as e:
   import inspect
   got_args = int(re.search("\d+.*(\d+)",str(e)).groups()[0])
   print "missing args:",inspect.getargspec(f).args[got_args:]
一个更好的方法是一个装饰师

def arg_decorator(fn):
   def func(*args,**kwargs):
       try: 
           return fn(*args,**kwargs)
       except TypeError:
           arg_spec = inspect.getargspec(fn)
           missing_named = [a for a in arg_spec.args if a not in kwargs]
           if arg_spec.defaults:
                    missing_args = missing_named[len(args): -len(arg_spec.defaults) ]
           else:
                    missing_args = missing_named[len(args):]
           print "Missing:",missing_args
   return func

@arg_decorator
def fn1(x,y,z):                      
      pass

def fn2(x,y):
    pass

arged_fn2 = arg_decorator(fn2)

fn1(5,y=2)
arged_fn2(x=1)

我认为这样做没有多大意义。由于程序员没有指定参数,因此引发了这样的异常。因此,如果您有意捕获异常,那么您也可以首先修复它

也就是说,在当前的Python 3版本中,抛出的
TypeError
确实提到调用中缺少哪些参数:

"f() missing 1 required positional argument: 'y'"
不幸的是,没有单独提到参数名称,因此您必须从字符串中提取它:

try:
    f(x=1)
except TypeError as e:
    if 'required positional argument' in e.args[0]:
        argumentNames = e.args[0].split("'")[1::2]
        print('Missing arguments are ' + argumentNames)
    else:
        raise # Re-raise other TypeErrors

正如Joran Beasley在评论中指出的那样,Python2不会告诉您缺少哪些参数,而是告诉您缺少多少参数。因此,无法从异常中判断调用中缺少了哪些参数。

一种更简洁的方法是将函数包装到另一个函数中,通过
*args、**kwargs
,然后在需要时使用这些值,而不是事后尝试重新构建它们。但是如果你不想那样做

正如poke的回答所解释的,在Python3.x中(非常早期的版本除外),这很简单。使用3.3+、与和朋友更轻松

在Python2.x中,没有办法做到这一点。该异常只有字符串
'f()在其
参数中正好取2个参数(1个给定)

除了…特别是在CPython2.x中,有足够的丑陋和脆弱的黑客是可能的

你有一个回溯,所以你有它的
tb\u帧
tb\u行
…这就是你所需要的一切。因此,只要源代码可用,该模块就可以轻松获得实际的函数调用表达式。然后,您只需解析它(via)就可以获得传递的参数,并与函数的签名进行比较(不幸的是,在2.x中获得签名并不像在3.3+中那么容易,但是在
f.func_默认值
f.func_code.co_argcount
等之间,您可以重构它)

但是如果来源不可用怎么办?在
tb\u frame.f\u code
tb\u lasti
之间,您可以找到函数调用在字节码中的位置。这个模块使得解析相对容易。特别是,就在调用之前,位置参数和关键字参数的名称-值对都被推送到堆栈上,因此您可以很容易地看到哪些名称被推,以及有多少位置值,并以这种方式重构函数调用。以同样的方式将其与签名进行比较


当然,这取决于关于CPython编译器如何构建字节码的一些假设。以各种不同的顺序进行操作是完全合法的,只要堆栈最终具有正确的值。所以,它很脆。但是我认为已经有更好的理由不这样做了。

除了处理例外,不可能做你想做的事情和处理关键字参数。这当然是WRTPython2.7

在Python中生成此消息的代码是:

PyErr_Format(PyExc_TypeError,
    "%.200s() takes %s %d "
    "argument%s (%d given)",
    PyString_AsString(co->co_name),
    defcount ? "at most" : "exactly",
    co->co_argcount,
    co->co_argcount == 1 ? "" : "s",
    argcount + kwcount);
取自第3056-3063行

正如您所看到的,对于异常缺少哪些参数,没有提供足够的信息<在此上下文中,code>co
是被调用的
PyCodeObject
。给出的唯一内容是一个字符串(如果愿意,可以对其进行解析),其中包含函数名、是否存在vararg、需要多少个参数以及给出了多少个参数。正如已经指出的,这并没有为您提供关于哪些参数没有给出的足够信息(在关键字参数的情况下)

类似于
inspect
或其他调试模块的功能可能能够为您提供足够的信息,说明调用了什么函数以及如何调用它,您可以从中找出哪些参数没有给出


不过,我还应该提到,几乎可以肯定的是,您提出的任何解决方案都将无法处理至少一些扩展模块方法(用C编写的方法),因为它们不提供参数信息作为对象的一部分。

@CrazyCasta这并没有真正回答这个问题……这是一个有用且相关的问题,但不是复制品。人们对投票结果非常满意,所以这些天……这是为哪个版本的Python设计的?对于非常早期的2.x、之后的2.x、非常早期的3.x和之后的3.x,答案是不同的。请注意,当前的Python3将为您提供这些信息。升级!:)@马吕斯:如果这只是一个练习……现在可能是开始玩回溯、框架、代码对象、
inspect.getsource
dis.dis
,等等的好时机。但实际上,我认为你最好先转到Python 3.4,在那里,所有这些都比2.7更容易、更清晰、更有趣,而您所获得的知识在将来将是有价值的。对于python 2.6来说,这是不正确的至少
aa()在python中正好使用了2个非关键字参数(给定1个)
indexer2@wim我在2中得到了TypeError。6@wim嗯,为了哪一行?这是在Python 3.1+(我想……可能是3.2+)中最简单也是最好的方法。它不适用于2.x或3.0,但在那里没有简单的方法。exeption消息中的数字不会告诉您缺少哪个参数,而是告诉您缺少多少参数。请尝试
f(y=5)