python 裝飾器

裝飾器(無參) (多裝飾器執(zhí)行由底向上)

  • 它是一個(gè)函數(shù)
  • 函數(shù)作為它的形參
  • 返回值也是一個(gè)函數(shù)
  • 可以使用@functionname方式,簡化調(diào)用

注:此處裝飾器的定義只是簡單總結(jié),并不準(zhǔn)確,只是方便理解


裝飾器是高階函數(shù),但裝飾器是對傳入函數(shù)的功能的裝飾(功能增強(qiáng))

import datetime
import time
def logger(fn):
    def wrapper(*args,**kwargs):
        'this is wrapper function'
        print('args={},kwargs={}'.format(args,kwargs))
        start = datetime.datetime.now()
        ret = fn(*args,**kwargs)
        duration = (datetime.datetime.now() - start).total_seconds()
        print('function {} took {}s'.format(fn.__name__,duration))
        return ret
    return wrapper

@logger
def add(x, y):
    'this is add function'
    return x + y


>>> add(5,5)
> args=(5, 5),kwargs={}
> function add took 0.0s

>>> print(add.__name__)
>>> print(add.__doc__)
> wrapper
> this is wrapper function

上面的例子中我們會發(fā)現(xiàn)add函數(shù)對象的屬性變成了wrapper函數(shù)的屬性,使用裝飾器我們是希望查看被封函數(shù)的屬性,所以這里我們就需要帶參裝飾器來解決這個(gè)問題


帶參裝飾器
  • 它是一個(gè)函數(shù)
  • 函數(shù)作為它的形參
  • 返回值是一個(gè)不帶參數(shù)的裝飾器函數(shù)
  • 使用functionname(參數(shù)列表) 方式調(diào)用
  • 可以看做在裝飾外層又加了一層函數(shù)
  • 需求:獲取函數(shù)的執(zhí)行時(shí)長,對時(shí)長超過閾(yu)值的函數(shù)記錄一下
import datetime
import time

def copy_properties(src):
    def _copy(dest):
        dest.__name__ = src.__name__
        dest.__doc__ = src.__doc__
        return dest
    return _copy

def logger(duration):
    def _logger(fn):
        @copy_properties(fn) 
        # wrapper = copy_properties(fn)(wrapper) => _copy(wrapper) => wrapper
        def wrapper(*args,**kwargs):
            ''' this is  wrapper function '''
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            print('so slow') if delta > duration else print('so fast')
            return ret

        return wrapper
    return _logger

@logger(6)
def add(x, y):
    ''' this is  add function '''
    time.sleep(3)
    return x + y 

>>> add(3,4)
>>> print(add.__doc__)
> so fast
> this is  add function 

注:這里我們通過copy_properties函數(shù)解決了上面屬性發(fā)生改變的問題。

  • 上面的例子我們還可以將記錄功能提取出來,這樣就可以通過外部提供的函數(shù)來靈活控制輸出
def logger(duration,func = lambda name,duration:print('{} took {}s'.format(name,duration))):
    def _logger(fn):
        @copy_properties(fn) 
        # wrapper = copy_properties(fn)(wrapper) => _copy(wrapper) => wrapper
        def wrapper(*args,**kwargs):
            ''' this is  wrapper function '''
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret

        return wrapper
    return _logger

在Python functools模塊中自帶了兩個(gè)函數(shù)可以幫我們更方便的解決使用裝飾器時(shí)函數(shù)屬性改變的問題

update_wrapper函數(shù)

functools.update_wrapper(wrapper, wrapped, assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'), updated=('__dict__',))

  • 類似copy_properties功能
  • wrapper 包裝函數(shù)、被更新者、wrapped 被包裝函數(shù),數(shù)據(jù)源
  • 元組WRAPPER_ASSIGNMENTS 中是要被覆蓋的屬性,模塊名、名稱、限定名、文檔、參數(shù)注解
  • 元組WRAPPER_UPDATES 中是要被更新的屬性,dict 屬性字典
  • 增加一個(gè)wrapped屬性,保留著wrapped函數(shù)
import datetime,time,functools
def logger(duration, func=lambda name,duration: print('{} took {}s'.format(name,duration))):
    def _logger(fn):
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return functools.update_wrapper(wrapper,fn)
    return _logger

@logger(5)  # add = logger(5)(add)
def add(x,y):
    time.sleep(2)
    return x+y

print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep='\n')


wraps 函數(shù)

functools.wraps(wrapped, assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'), updated=('__dict__',))
  • 類似copy_properties功能
  • wrapped 被包裝函數(shù)
  • 元組WRAPPER_ASSIGNMENTS 中是要被覆蓋的屬性,模塊名、名稱、限定名、文檔、參數(shù)注解
  • 元組WRAPPER_UPDATES 中是要被更新的屬性,dict 屬性字典
  • 增加一個(gè)wrapped屬性,保留著wrapped函數(shù)
import datetime,time,functools
def logger(duration, func=lambda name,duration: print('{} took {}s'.format(name,duration))):
    def _logger(fn):
        @functools.wraps(fn)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return wrapper
    return _logger

@logger(5)  # add = logger(5)(add)
def add(x,y):
    time.sleep(2)
    return x+y

print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep='\n')

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容