一.update_wrapper
該函數(shù)用于更新包裝函數(shù)(wrapper),使它看起來像被包裝的原函數(shù)一樣。
該函數(shù)主要用于裝飾器函數(shù)的定義中,置于包裝函數(shù)之前。如果沒有對包裝函數(shù)進(jìn)行更新,那么被裝飾后的函數(shù)所具有的元信息就會變?yōu)榘b函數(shù)的元信息,而不是原函數(shù)的元信息。
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
- wrapper 包裝函數(shù),被更新者
- wrapped 被包裝函數(shù),數(shù)據(jù)源
- assigned 包裝函數(shù)需要替換的屬性,替換的內(nèi)容直接來自于被包裝函數(shù)
- updated 指定包裝函數(shù)需要更新的屬性,更新的內(nèi)容需要對照被包裝函數(shù)進(jìn)行更新。
該函數(shù)自動在wrapper函數(shù)添加一個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(1)
return x + y
print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')
二.functools.wraps
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
這是一個便捷函數(shù),定義一個wrapper函數(shù)時,作為函數(shù)裝飾器調(diào)用update_wrapper()。它等價于partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated),返回一個形參wrapped 有默認(rèn)值的新的update_warpper 函數(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(1)
return x + y
print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')
三.functools.reduce
reduce(function, sequence, initial=None) -> value
- function 這個函數(shù)是要有兩個參數(shù)的
- sequence 序列
- initial 如果初始值沒有,拿sequence第一個元素
等價于
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
value = next(it)
else:
value = initializer
for element in it:
value = function(value, element)
return value
具體應(yīng)用如下:
from functools import reduce
def add(x,y):
return x + y
reduce(add,range(5))
四.functools.partial 偏函數(shù)
functools.partial(func, *args, **keywords) -> newfunc
把函數(shù)部分的參數(shù)固定下來,相當(dāng)于為部分的參數(shù)添加了一個固定的默認(rèn)值,形成一個新的函數(shù)并且返回,是對原函數(shù)的封裝
partial 函數(shù)本質(zhì)
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords): # 包裝函數(shù)
newkeywords = keywords.copy() newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func # 保留原函數(shù)
newfunc.args = args # 保留原函數(shù)的位置參數(shù)
newfunc.keywords = keywords # 保留原函數(shù)的關(guān)鍵字參數(shù)參數(shù)
return newfunc
五.functools.lru_cache
functools.lru_cache(maxsize=128, typed=False)
- Least-recently-used 裝飾器。
- 如果maxsize 設(shè)置為None,則金庸LRU功能,并且緩存可以無限制增長,當(dāng)maxsize是2的冪的時候,LRU功能執(zhí)行最好
- 如果typed為True,則不同類型的函數(shù)參數(shù)將單獨緩存,例如,f(3)和f(3.0)將被視為具有同結(jié)果的不同調(diào)用
- 使用前提
1.同樣的函數(shù)參數(shù)一定得到同樣的結(jié)果
2.函數(shù)執(zhí)行時間很長,且要多次執(zhí)行 - 本質(zhì)是函數(shù)調(diào)用的參數(shù) ->返回值
- 缺點
- 不支持緩存過期,key無法過期,失效
- 不支持清除操作
- 不支持分布式,是一個單機的緩存
- 使用場景,單機上需要空間換時間的地方,可以用緩存來將計算變成快速的查詢
裝飾器用一個有記憶的調(diào)用包裝一個函數(shù),它可以保存最近maxsize次調(diào)用。當(dāng)使用同樣的參數(shù)定期調(diào)用費時或I/O綁定的函數(shù)時,它可以節(jié)省時間。
因為使用字典緩存結(jié)果,所以函數(shù)的位置和關(guān)鍵字必須是可以哈希的。
為了幫助測量緩存的有效性并且調(diào)整maxsize 參數(shù),可以使用cache_info() 返回一個命名元組,包含hits,misses,maxsize和currsize。在多線程環(huán)境中,hits和misses是近似值。
裝飾器還提供了cache_clean() 用于清除緩存。