2016-01-16 75 views
3

我是python和装饰器的新手,并且难于编写一个装饰器,它不仅报告了args和kwargs,而且还保留了未改变的默认kwargs。python装饰器显示传递和默认kwargs

这是我到目前为止。

def document_call(fn): 
    def wrapper(*args, **kwargs): 
     print 'function %s called with positional args %s and keyword args %s' % (fn.__name__, args, kwargs) 
     return fn(*args, **kwargs) 
    return wrapper 

@document_call 
def square(n, trial=True, output=False): 
    # kwargs are a bit of nonsense to test function 
    if not output: 
     print 'no output' 
    if trial: 
     print n*n 

square(6) # with this call syntax, the default kwargs are not reported 
# function square called with positional args (6,) and keyword args {} 
# no output 
36 

square(7,output=True) # only if a kwarg is changed from default is it reported 
# function square called with positional args (7,) and keyword args {'output': True} 
49 

“问题”是这个装饰报道说,在呼叫方通过,但没有报告在广场定义中定义的默认kwargs的ARGS。报告kwargs的唯一方法是如果它们从默认值改变,即传递给方形调用。

对于我如何获得方形定义中的kwargs的任何建议也有报道?

后续检查建议,这有助于我下面的解决方案后编辑。我改变了位置参数的输出以包含他们的名字,因为我认为它使得输出更容易理解。

import inspect 
def document_call(fn): 
    def wrapper(*args, **kwargs):    
      argspec = inspect.getargspec(fn) 
      n_postnl_args = len(argspec.args) - len(argspec.defaults) 
     # get kwargs passed positionally 
     passed = {k:v for k,v in zip(argspec.args[n_postnl_args:], args[n_postnl_args:])} 
     # update with kwargs 
     passed.update({k:v for k,v in kwargs.iteritems()})    
     print 'function %s called with \n positional args %s\n passed kwargs %s\n default kwargs %s' % (
       fn.__name__, {k:v for k,v in zip(argspec.args, args[:n_postnl_args])}, 
       passed, 
       {k:v for k,v in zip(argspec.args[n_postnl_args:], argspec.defaults) if k not in passed})   
     return fn(*args, **kwargs) 
return wrapper 

这是一个很好的学习经验。看到针对同一问题的三种不同解决方案是很好的。感谢Answerers!

+0

@Martijn,谢谢;我已经阅读了答案的前两行,并且会消失,学习所有关于检查的内容,并且看看我想出了什么。然后会回来看看如何真正做到这一点;-) – user5799671

回答

5

您将不得不反思您包装的功能以读取默认值。你可以用inspect.getargspec() function来做到这一点。

该函数返回一个元组,其中包含所有参数名称的序列和一系列默认值。参数名称的最后一个与缺省值配对以形成名称 - 缺省值对;你可以用它来创建一个字典,并提取未使用的默认设置从那里:

import inspect 

argspec = inspect.getargspec(fn) 
positional_count = len(argspec.args) - len(argspec.defaults) 
defaults = dict(zip(argspec.args[positional_count:], argspec.defaults)) 

你需要考虑到位置参数可以指定默认的参数太多,所以舞蹈找出关键字参数被一个稍微复杂些,但看起来是这样的:

def document_call(fn): 
    argspec = inspect.getargspec(fn) 
    positional_count = len(argspec.args) - len(argspec.defaults) 
    defaults = dict(zip(argspec.args[positional_count:], argspec.defaults)) 
    def wrapper(*args, **kwargs): 
     used_kwargs = kwargs.copy() 
     used_kwargs.update(zip(argspec.args[positional_count:], args[positional_count:])) 
     print 'function %s called with positional args %s and keyword args %s' % (
      fn.__name__, args[:positional_count], 
      {k: used_kwargs.get(k, d) for k, d in defaults.items()}) 
     return fn(*args, **kwargs) 
    return wrapper 

这决定PARAMATERS实际上是从两个传入的位置参数和关键字参数使用哪些关键字,然后翻出默认值对于那些不使用。

演示:

>>> square(39) 
function square called with positional args (39,) and keyword args {'trial': True, 'output': False} 
no output 
1521 
>>> square(39, False) 
function square called with positional args (39,) and keyword args {'trial': False, 'output': False} 
no output 
>>> square(39, False, True) 
function square called with positional args (39,) and keyword args {'trial': False, 'output': True} 
>>> square(39, False, output=True) 
function square called with positional args (39,) and keyword args {'trial': False, 'output': True} 
1

由于装饰功能wrapper需要任何说法,只是传递的一切,当然它不知道包装的函数的参数和它的默认值什么。

所以,如果没有真正查看装饰功能,您将无法获得此信息。幸运的是,您可以使用inspect模块计算出封装函数的默认参数。

您可以使用inspect.getargspec函数获取有关函数签名中默认参数值的信息。你只需要与参数名称正确地匹配起来:

def document_call(fn): 
    argspec = inspect.getargspec(fn) 
    defaultArguments = list(reversed(zip(reversed(argspec.args), reversed(argspec.defaults)))) 

    def wrapper(*args, **kwargs): 
     all_kwargs = kwargs.copy() 
     for arg, value in defaultArguments: 
      if arg not in kwargs: 
       all_kwargs[arg] = value 

     print 'function %s called with positional args %s and keyword args %s' % (fn.__name__, args, all_kwargs) 

     # still make the call using kwargs, to let the function handle its default values 
     return fn(*args, **kwargs) 
    return wrapper 

请注意,您仍然可以改善这是现在你是分开处理的位置和命名参数。例如,在您的square函数中,您也可以通过在n之后将它作为位置参数来设置trial。这将使其不出现在kwargs。所以你必须将你的kwargs的位置参数匹配来获得完整的信息。您可以从argspec获取有关头寸的所有信息。