functools.wraps是作什麼的?

對另外一個問題的答案發表評論時,有人說他們不肯定functools.wraps在作什麼。 因此,我問這個問題,以便在StackOverflow上有它的記錄以備未來參考: functools.wraps是什麼? html


#1樓

我常常將類而不是函數用於裝飾器。 我在這方面遇到了麻煩,由於對象不會具備函數所指望的全部相同屬性。 例如,一個對象將沒有屬性__name__ 。 我有一個特定的問題,很難跟蹤Django報告錯誤「對象沒有屬性' __name__ '」的位置。 不幸的是,對於類風格的裝飾器,我認爲@wrap不會勝任。 相反,我建立了一個基本的裝飾器類,以下所示: python

class DecBase(object):
    func = None

    def __init__(self, func):
        self.__func = func

    def __getattribute__(self, name):
        if name == "func":
            return super(DecBase, self).__getattribute__(name)

        return self.func.__getattribute__(name)

    def __setattr__(self, name, value):
        if name == "func":
            return super(DecBase, self).__setattr__(name, value)

        return self.func.__setattr__(name, value)

此類將全部屬性調用代理到要修飾的函數。 所以,您如今能夠建立一個簡單的裝飾器來檢查是否指定了2個參數,以下所示: git

class process_login(DecBase):
    def __call__(self, *args):
        if len(args) != 2:
            raise Exception("You can only specify two arguments")

        return self.func(*args)

#2樓

使用裝飾器時,您將一個功能替換爲另外一個功能。 換句話說,若是您有一個裝飾器 github

def logged(func):
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

而後當你說 app

@logged
def f(x):
   """does some math"""
   return x + x * x

這和說的徹底同樣 函數

def f(x):
    """does some math"""
    return x + x * x
f = logged(f)

而且將函數f替換爲with_logging函數。 不幸的是,這意味着若是您而後說 this

print(f.__name__)

它將顯示with_logging由於那是新函數的名稱。 實際上,若是您查看f的文檔字符串,它將爲空,由於with_logging沒有文檔字符串,所以您編寫的文檔字符串將再也不存在。 另外,若是您查看該函數的pydoc結果,它將不會被列爲採用一個參數x ; 相反,它將被列爲帶有*args**kwargs由於with_logging須要這樣作。 spa

若是使用裝飾器老是意味着丟失有關功能的信息,那將是一個嚴重的問題。 這就是爲何咱們有functools.wraps的緣由。 它採用了裝飾器中使用的功能,並添加了複製功能名稱,文檔字符串,參數列表等的功能。因爲wraps自己就是裝飾器,所以如下代碼能夠正確執行操做: 代理

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

print(f.__name__)  # prints 'f'
print(f.__doc__)   # prints 'does some math'

#3樓

  1. 先決條件:您必須知道如何使用裝飾器,尤爲是包裝器。 該評論對其進行了清晰解釋,或者該連接也對其進行了很好的解釋。 code

  2. 每當咱們使用For例如:@wraps後跟咱們本身的包裝函數。 根據此連接中給出的詳細信息,它表示

functools.wraps是方便函數,用於在定義包裝函數時調用update_wrapper()做爲函數裝飾器。

它等效於partial(update_wrapper,wraped = wrapped,assigned = assigned,updated = updated)。

所以,@ wraps裝飾器實際上會調用functools.partial(func [,* args] [,** keywords])。

functools.partial()定義說

partial()用於部分函數應用程序,該函數「凍結」函數的參數和/或關鍵字的某些部分,從而生成具備簡化簽名的新對象。 例如,partial()可用於建立行爲相似於int()函數的可調用對象,其中基本參數默認爲兩個:

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18

這使我得出如下結論:@wraps調用了partial(),並將包裝函數做爲參數傳遞給它。 最後,partial()返回簡化版本,即包裝函數內部的對象,而不是包裝函數自己。


#4樓

簡而言之, functools.wraps只是一個常規函數。 讓咱們考慮這個官方示例 。 藉助於源代碼 ,咱們能夠看到有關實現和運行步驟的更多詳細信息,以下所示:

  1. wraps(f)返回一個對象,例如O1 。 它是Partial類的對象
  2. 下一步是@ O1 ... ,這是python中的修飾符符號。 它的意思是

wrapper = O1 .__ call __(wrapper)

檢查的執行__call__ ,咱們看到這一步,(左側) 包裝成爲對象形成的self.func(* self.args,* ARGS,** newkeywords)檢查在__new__ O1的建立後,咱們知道self.func是功能update_wrapper。 它使用參數* args (右側包裝器 )做爲其第一個參數。 檢查update_wrapper的最後一步,能夠看到返回了右側包裝器 ,並根據須要修改了一些屬性。


#5樓

這是關於包裝的源代碼:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')

WRAPPER_UPDATES = ('__dict__',)

def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):

    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        setattr(wrapper, attr, getattr(wrapped, attr))
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

   Returns a decorator that invokes update_wrapper() with the decorated
   function as the wrapper argument and the arguments to wraps() as the
   remaining arguments. Default arguments are as for update_wrapper().
   This is a convenience function to simplify applying partial() to
   update_wrapper().
    """
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)
相關文章
相關標籤/搜索