2016-08-31 27 views
1

我有一個基礎裝飾器,它需要參數,但也是由其他裝飾器構建的。我似乎無法確定在哪裏放置functools.wraps以保留裝飾函數的完整簽名。將functools.wraps應用到嵌套包裝

import inspect 
from functools import wraps 

# Base decorator 
def _process_arguments(func, *indices): 
    """ Apply the pre-processing function to each selected parameter """ 
    @wraps(func) 
    def wrap(f): 
     @wraps(f) 
     def wrapped_f(*args): 
      params = inspect.getargspec(f)[0] 

      args_out = list() 
      for ind, arg in enumerate(args): 
       if ind in indices: 
        args_out.append(func(arg)) 
       else: 
        args_out.append(arg) 

      return f(*args_out) 
     return wrapped_f 
    return wrap 


# Function that will be used to process each parameter 
def double(x): 
    return x * 2 

# Decorator called by end user 
def double_selected(*args): 
    return _process_arguments(double, *args) 

# End-user's function 
@double_selected(2, 0) 
def say_hello(a1, a2, a3): 
    """ doc string for say_hello """ 
    print('{} {} {}'.format(a1, a2, a3)) 

say_hello('say', 'hello', 'arguments') 

這段代碼的結果應該是

saysay hello argumentsarguments 

但是,在運行上say_hello幫助給了我:

say_hello(*args, **kwargs) 
    doc string for say_hello 

一切都被保留,除參數名稱。

看來我只需要在某處添加另一個@wraps(),但是在哪裏呢?

+0

你使用python 2或Python 3? – bravosierra99

回答

0

direprobs是在正確的無functools量包裹會讓我在那裏。 bravosierra99指出我有些相關的例子。但是,我無法找到一個嵌套裝飾器上的簽名保存的例子,其中外部裝飾器接受參數。

與參數裝飾的comments on Bruce Eckel's post給了我實現我想要的結果,最大的提示。

的關鍵是從我_process_arguments函數中取出中間的功能,並把它的參數在未來,嵌套函數。它那種對我來說很有意義,現在...但它的工作原理:

import inspect 
from decorator import decorator 

# Base decorator 
def _process_arguments(func, *indices): 
    """ Apply the pre-processing function to each selected parameter """ 
    @decorator 
    def wrapped_f(f, *args): 
     params = inspect.getargspec(f)[0] 

     args_out = list() 
     for ind, arg in enumerate(args): 
      if ind in indices: 
       args_out.append(func(arg)) 
      else: 
       args_out.append(arg) 

     return f(*args_out) 
    return wrapped_f 


# Function that will be used to process each parameter 
def double(x): 
    return x * 2 

# Decorator called by end user 
def double_selected(*args): 
    return _process_arguments(double, *args) 

# End-user's function 
@double_selected(2, 0) 
def say_hello(a1, a2,a3): 
    """ doc string for say_hello """ 
    print('{} {} {}'.format(a1, a2, a3)) 

say_hello('say', 'hello', 'arguments') 
print(help(say_hello)) 

而結果:

saysay hello argumentsarguments 
Help on function say_hello in module __main__: 

say_hello(a1, a2, a3) 
    doc string for say_hello 
0

我嘗試了這一點:

>>> from functools import wraps 
>>> def x(): print(1) 
... 
>>> @wraps(x) 
... def xyz(a,b,c): return x 


>>> xyz.__name__ 
'x' 
>>> help(xyz) 
Help on function x in module __main__: 

x(a, b, c) 

AFAIK,這有沒有關係wraps本身,而是涉及到help的問題。事實上,因爲help檢查您的對象提供的信息,包括__doc__和其他屬性,這就是爲什麼你會得到這種行爲,雖然你的包裝函數有不同的參數列表。雖然,wraps不會更新自動(參數列表),它到底是更新這個元組和__dict__這在技術上是對象的命名空間:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', 
         '__annotations__') 
WRAPPER_UPDATES = ('__dict__',) 

如果你不知道如何wraps工作,大概如果您從標準庫中讀取源代碼,它會有所幫助:functools.py

看來我只是需要添加另一個@wraps()的地方,但在哪裏?

不,你不需要添加其他wraps在你的代碼,help正如我上面說通過檢查你的對象工作的方式。函數的參數與代碼對象(__code__)相關聯,因爲函數的參數在該對象中存儲/表示,wraps無法將包裝的參數更新爲像包裝函數(繼續上面的示例):

>>> xyz.__code__.co_varnames 

>>> xyz.__code__.co_varnames = x.__code__.co_varnames 
AttributeError: readonly attribute 

如果help顯示的功能xyz有這個參數列表(),而不是(a, b, c)那麼這顯然是錯誤的!同樣適用於wraps,將包裝的參數列表更改爲包裝,會很麻煩!所以這應該不是一個問題。

>>> @wraps(x, ("__code__",)) 
... def xyz(a,b,c): pass 
... 

>>> help(xyz) 
Help on function xyz in module __main__: 

xyz() 

xyz()回報x()

>>> xyz() 
1 

瞭解其他的參考看看這個問題,或Python文檔

What does functools.wraps do?