国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

python裝飾器

2019-11-14 17:07:58
字體:
來源:轉載
供稿:網友

裝飾器入門

以顯示時間為例子:定義一個顯示當前時間的函數:

from datetime import datetimedef now():     PRint(datetime.utcnow())

當調用now()的時候,會顯示出當前時間

我們想在顯示時間之前(或之后),添加一點功能,但是我們又不想更改函數和整體結構。此時,我們就需要裝飾器來實現這個功能
裝飾器的作用在于 在代碼運行期間動態增加功能(在執行某個函數之前或之后動態的增加功能)
現在,我們想在顯示時間之前顯示某些東西(這里我顯示調用的函數名)

import functoolsdef log(func):    @functools.wraps(func)    def wrapper(*args, **kw):        print('call %s()' % fun.__name__)        return fun(*args, **kw)    return wrapper@logdef now():    print(datetime.utcnow())

此時我們調用now()函數,將返回:

call now()當前時間

放到now()的定義處,相當于執行了 now = log(now)

如果裝飾器本身需要傳入參數,我們需要增加一個函數:

import functoolsdef log(text):     def decorator(func):          @functools.wraps(func)          def wrapper(*args, **kw):               print('The function is %s, %s' % (func.__name__, text), end=' ')               return func(*args, **kw)          return wrapper     return decorator@log(‘The time is ’)def now():    print(datetime.utcnow())

此時我們調用now()函數,將返回:

The function is now, the time is 當前時間

三層嵌套相當于執行了now = log(text)(now)
首先執行log(text),返回一個decorator函數,然后執行decorator(now),返回一個wrapper函數
我們可以嘗試輸入now.__name__來查看現在now的函數,結果是‘wrapper’
在帶參數的裝飾器中,如果未加入@functools.wraps(func) 則用now.__name__查看,結果是‘wrapper’, 本例中加入了wraps,所以使用now.__name__查看, 結果是‘now’

functools.wraps(func)源碼分析

在運行以上程序的時候,對裝飾器設置斷點,逐步查詢(以沒有參數的裝飾器為例)
當運行到functools.wraps(func)時,會進入functools.py的wraps函數:

def wraps(wrapped,          assigned = WRAPPER_ASSIGNMENTS,          updated = WRAPPER_UPDATES):    return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)

參數wrapped是傳入的func參數,其他兩個參數如下:

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

wraps函數會返回一個partial函數。
partial函數的作用是把wrapped、assigned、updated這三個參數傳給update_wrapper函數,然后返回一個新函數
partial是偏函數,當傳入的參數為**kw時,其作用是把一個函數的某些參數給固定住(即設置默認值),返回一個新函數;當傳入的參數為*args時,會把該參數自動加入到原函數中,返回一個新函數
在wraps源碼中,可以發現,使用的是傳入**kw參數
在partial函數中,我們可以看到一個update_wrapper函數和三個參數

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 newfunctry:    from _functools import partialexcept ImportError:    pass

根據partical函數的源碼來看,里面提供的newfunc函數的作用就是生成一個新函數
接下來看函數update_wrapper

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, {}))    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it    # from the wrapped function when updating __dict__    wrapper.__wrapped__ = wrapped    # Return the wrapper so this can be used as a decorator via partial()    return wrapper

在函數update_wrapper中,(func)后面緊跟著定義的函數,wrapped表示函數wraps傳入的參數func(也就是要修飾的目標函數)
該update_wrapper函數會把wrapped中的assigned復制給wrapper,并更新wrapper中的__dict__,返回wrapper函數
update_wrapper函數是functools.wraps主要功能提供者,負責拷貝原函數的屬性(assigned和updated)

總結一下:

functools.wraps(func)是裝飾器的裝飾器,主要作用是用來包裝函數wrapper,通過拷貝原函數func的屬性給wrapper的方式,使被包裝的函數wrapper更像原函數func
@functools.wraps(func) = 把update_wrapper函數與func以及其他參數綁定,生成一個新函數newfunc;然后執行newfunc函數(實際運行進行了參數固定的update_wrapper函數),把func更新到wrapper中,使得外部可以直接調用wrapper((func)定義),從而達到動態地增加函數的功能的作用

functools.wraps(func)是裝飾器的裝飾器:

原函數 @functools.wraps(func)
def wrapper():
...
func()
...

等價形式 wrapper = functools.wraps(func)(wrapper) #類似于前面講的帶參數的裝飾器

等價形式 wrapper = partial(update_wrapper, wrapped=func, assigned=assigned, updated=updated)(wrapper)

進一步等價 wrapper = update_wrapper(wrapper=wrapper, wrapped=func, assigned=assigned, updated=updated)


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 望奎县| 公主岭市| 涞水县| 永泰县| 合江县| 芜湖市| 怀化市| 蓝山县| 宜君县| 红桥区| 宿松县| 宣城市| 平罗县| 富平县| 红原县| 杭州市| 苗栗市| 和顺县| 沧州市| 习水县| 东海县| 扶沟县| 朝阳市| 藁城市| 宣城市| 南阳市| 万盛区| 临潭县| 台湾省| 三门县| 敦化市| 凤台县| 云林县| 武汉市| 胶南市| 呼图壁县| 合阳县| 宁武县| 临澧县| 济南市| 仙居县|