2016-01-16 77 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獲取有關頭寸的所有信息。