1. 程式人生 > >python 裝飾器@wraps

python 裝飾器@wraps

預備知識

在瞭解wraps修飾器之前,我們首先要了解partialupdate_wrapper這兩個函式,因為在wraps的程式碼中,用到了這兩個函式。

partial

首先說partial函式,在官方文件的描述中,這個函式的宣告如下:functools.partial(func, *args, **keywords)。它的作用就是返回一個partial物件,當這個partial物件被呼叫的時候,就像通過func(*args, **kwargs)的形式來呼叫func函式一樣。如果有額外的 位置引數(args) 或者 關鍵字引數(*kwargs) 被傳給了這個partial物件,那它們也都會被傳遞給func

函式,如果一個引數被多次傳入,那麼後面的值會覆蓋前面的值。

個人感覺這個函式很像C++中的bind函式,都是把某個函式的某個引數固定,從而構造出一個新的函式來。比如下面這個例子:

from functools import partial

def add(x:int, y:int):
    return x+y

# 這裡創造了一個新的函式add2,只接受一個整型引數,然後將這個引數統一加上2
add2 = partial(add, y=2)

add2(3)  # 這裡將會輸出5

這個函式是使用C而不是Python實現的,但是官方文件中給出了Python實現的程式碼,如下所示,大家可以進行參考:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

update_wrapper

接下來,我們再來聊一聊update_wrapper這個函式,顧名思義,這個函式就是用來更新修飾器函式的,具體更新些什麼呢,我們可以直接把它的原始碼搬過來看一下:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    wrapper.__wrapped__ = wrapped
    return wrapper

大家可以發現,這個函式的作用就是從 被修飾的函式(wrapped) 中取出一些屬性值來,賦值給 修飾器函式(wrapper) 。為什麼要這麼做呢,我們看下面這個例子。

自定義修飾器v1

首先我們寫個自定義的修飾器,沒有任何的功能,僅有文件字串,如下所示:

def wrapper(f):
    def wrapper_function(*args, **kwargs):
        """這個是修飾函式"""
        return f(*args, **kwargs)
    return wrapper_function
    
@wrapper
def wrapped():
    """這個是被修飾的函式"""
    print('wrapped')

print(wrapped.__doc__)  # 輸出`這個是修飾函式`
print(wrapped.__name__)  # 輸出`wrapper_function`

從上面的例子我們可以看到,我想要獲取wrapped這個被修飾函式的文件字串,但是卻獲取成了wrapper_function的文件字串,wrapped函式的名字也變成了wrapper_function函式的名字。這是因為給wrapped新增上@wrapper修飾器相當於執行了一句wrapped = wrapper(wrapped),執行完這條語句之後,wrapped函式就變成了wrapper_function函式。遇到這種情況該怎麼辦呢,首先我們可以手動地在wrapper函式中更改wrapper_function__doc____name__屬性,但聰明的你肯定也想到了,我們可以直接用update_wrapper函式來實現這個功能。

自定義修飾器v2

我們對上面定義的修飾器稍作修改,添加了一句update_wrapper(wrapper_function, f)

from functools import update_wrapper

def wrapper(f):
    def wrapper_function(*args, **kwargs):
        """這個是修飾函式"""
        return f(*args, **kwargs)
    update_wrapper(wrapper_function, f)  # <<  添加了這條語句
    return wrapper_function
    
@wrapper
def wrapped():
    """這個是被修飾的函式"""
    print('wrapped')


print(wrapped.__doc__)  # 輸出`這個是被修飾的函式`
print(wrapped.__name__)  # 輸出`wrapped`

此時我們可以發現,__doc____name__屬性已經能夠按我們預想的那樣顯示了,除此之外,update_wrapper函式也對__module____dict__等屬性進行了更改和更新。

wraps修飾器

OK,至此,我們已經瞭解了partialupdate_wrapper這兩個函式的功能,接下來我們翻出wraps修飾器的原始碼:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

沒錯,就是這麼的簡單,只有這麼一句,我們可以看出,wraps函式其實就是一個修飾器版的update_wrapper函式,它的功能和update_wrapper是一模一樣的。我們可以修改我們上面的自定義修飾器的例子,做出一個更方便閱讀的版本。

自定義修飾器v3

from functools import wraps

def wrapper(f):
    @wraps(f)
    def wrapper_function(*args, **kwargs):
        """這個是修飾函式"""
        return f(*args, **kwargs)
    return wrapper_function
    
@wrapper
def wrapped():
    """這個是被修飾的函式
    """
    print('wrapped')

print(wrapped.__doc__)  # 輸出`這個是被修飾的函式`
print(wrapped.__name__)  # 輸出`wrapped`

至此,我想大家應該明白wraps這個修飾器的作用了吧,就是將 被修飾的函式(wrapped) 的一些屬性值賦值給 修飾器函式(wrapper) ,最終讓屬性的顯示更符合我們的直覺。

相關推薦

python 裝飾@wraps

預備知識 在瞭解wraps修飾器之前,我們首先要了解partial和update_wrapper這兩個函式,因為在wraps的程式碼中,用到了這兩個函式。 partial 首先說partial函式,在官方文件的描述中,這個函式的宣告如下:functools.partia

Python 裝飾函式 wraps 利用快取查詢原理,加速遞迴

# 裝飾器函式 def foo(fn): def wrappers(): print("hello, %s" %fn.__name__) fn() print("bye, %s" %fn.__name__) return wrap

python裝飾擴充套件之functools.wraps

我們知道函式被裝飾器,裝飾後,所有的屬性,以及內建函式就失效了。 原因是函式型別變成了warpper型別   示例1:不帶wraps裝飾器示例 def warfunc(func): def warpper(*args,**kwargs): print('wa

python裝飾中@wraps作用--修復被裝飾後的函式名等屬性的改變

Python裝飾器(decorator)在實現的時候,被裝飾後的函式其實已經是另外一個函數了(函式名等函式屬性會發生改變),為了不影響,Python的functools包中提供了一個叫wraps的decorator來消除這樣的副作用。寫一個decorator的時候,最好在實現之前加上functools的wra

python裝飾wraps作用

rap docstring cst class too called ted rom style 不加: from functools import wraps def my_decorator(func): def wper(*args, **kwargs):

python 9-2 如何為被裝飾的函式儲存元資料,使用標準庫functools中的裝飾wraps 裝飾內部包裹函式

9-2 如何為被裝飾的函式儲存元資料 解決方案: 使用標準庫functools中的裝飾器wraps 裝飾內部包裹函式,可以 制定將原函式的某些屬性,更新到包裹函式的上面 其實也可以通過 wr

021day--python裝飾

輸出 for 購物 c函數 blog led 運行時 index 基本框架 一、裝飾器含義   裝飾器本質就是函數,為其它函數添加附加功能 二、裝飾器原則   1.不修改被修飾函數的代碼   2.不修改被修飾函數的調用方式 三、裝飾器知識   裝飾器 = 高階函數

python-裝飾,類與對象,私有字段,析構,__call__,繼承,多繼承,接口

裝飾器 類與對象 私有字段 析構 __call__ 繼承 1、裝飾器執行流程裝飾器:將原函數替換為wrapper函數def outer()@outer --- func1作為參數傳入outer()def wrapper() --- wrapper()放入內存return wrapp

python--裝飾詳解

blog 內容 class align fun turn strip 叠代器 ros Python---裝飾器詳解 定義: 本質上是一個函數。作用是用來裝飾另一個函數(即被裝飾函數),給被裝飾函數添加功能。前提是不能改變被裝飾函數的源代碼和調用方式。這樣的一個函數稱之為裝飾

Python裝飾詳解

def 功能 style out return 裝飾器 代碼 方法 情況 首先是不使用裝飾器的情況,又需要在不修改原函數的情況話修改函數結果 1 def outer(func): 2 def inner(): 3 print("Hello")

python-裝飾

驗證 定義 總結 是把 裝飾 ron highlight class 老板 一、介紹 首先我們先來看一個簡單的例子,在基礎平臺中有一個home()和tv()函數,在業務平臺中調用此函數時,給出了響應的打印內容: 基礎平臺: def home(): print(‘w

python---裝飾

等等 value success star invalid user wrap net ces python裝飾器要點: 1. 裝飾器能夠給被裝飾的函數在不改變調用方式的情況下,增加功能,如日誌,計時等等 2. 被裝飾函數包含有不帶參數的,帶參數的 3. 裝飾器本身也分為不

python裝飾

要去 什麽 改變 而且 python 入門 class 定義 博客 網上面有很多優秀的文章寫得很好,但是每個人的思路和接受的方式都不一樣,我選用了自己能看得懂再加上自己的理解寫了這篇博客,將分為多步實現對裝飾器的理解,作為新手入門級別,另外會在結束後,給上我認為優秀文章的鏈

python-------裝飾

open isp over get spl 函數功能 style 先來 hide 一、簡單的裝飾器 1.為什麽要使用裝飾器呢?   裝飾器的功能:在不修改原函數及其調用方式的情況下對原函數功能進行擴展   裝飾器的本質:就是一個閉包函數 那麽我們先來看一個簡單的裝飾器:實現

python----------裝飾應用練習

pla spa opened lose 多個 hdd lib 列表 -s 1.編寫裝飾器,為多個函數加上認證的功能(用戶的賬號密碼來源於文件),要求登錄成功一次,後續的函數都無需再輸入用戶名和密碼註意:從文件中讀出字符串形式的字典,可以用eval(‘{"name":"ego

Python 裝飾

all *args 寫法 python的函數 日誌 方便 插入 3層 組合   裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象.   經常用於有切面需求的場景,比如:插入日誌、性能測試、事務處理

Python裝飾主要用法

**kwargs div odi 功能 func erro utf spa 情況 #!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ = ‘人生入戲‘ user = "admin" passwd = "123

python裝飾(2)

裝飾器 () 方式 cti 調用 st2 rgs 顯示 clas 1.以下代碼,bar作為參數被test2調用。bar的原代碼沒變,但調用方式從bar()變成test2(bar) 不符合裝飾器定義 1 __author__ = "csy" 2 3 def ba

python裝飾(3)

urn python裝飾器 裝飾 int 裝飾器 func 實現 ret test 另一種實現方式: 1 __author__ = "csy" 2 3 def test2(func): 4 def test1(): 5 func()

Python # 裝飾

als style div turn 通過 sta () pre ndt ### 現在我有一個簡單的myfunc函數,現在我想對myfunc函數增加功能。下面我們增加一個deco的功能。 import time def deco(func): startTime