摘要:比較函數(shù)是接收兩個(gè)參數(shù)進(jìn)行比較的函數(shù),返回一個(gè)負(fù)數(shù)表示,返回表示,返回一個(gè)正數(shù)表示。表示緩存大小限制,確保不會(huì)無(wú)限制增長(zhǎng)。大致等同于用于凍結(jié)函數(shù)的部分位置參數(shù)和或關(guān)鍵字參數(shù)而產(chǎn)生一個(gè)代表某部分函數(shù)功能的簡(jiǎn)化標(biāo)志。
functools模塊提供了某些高階函數(shù)(high-order function)。
functools.cmp_to_key(func)比較函數(shù)是接收兩個(gè)參數(shù)進(jìn)行比較的函數(shù),返回一個(gè)負(fù)數(shù)表示<,返回0表示=,返回一個(gè)正數(shù)表示>。key函數(shù)接收一個(gè)參數(shù)并返回另一個(gè)值作為進(jìn)行排序的鍵。
將比較函數(shù)轉(zhuǎn)換為key函數(shù),常在用到key關(guān)鍵字的函數(shù)如sotred(), min(), heapq.nlargest(), itertools,groupby()中使用。cmp_to_key主要作為過(guò)渡工具將python2中支持的比較函數(shù)進(jìn)行轉(zhuǎn)換。
def numeric_compare(x, y): return x - y # python2 print(sorted([5, 2, 4, 1, 3], cmp=numeric_compare)) # python3 print(sorted([5, 2, 4, 1, 3], key=cmp_to_key(numeric_compare)))@functools.lru_cache(maxsize=128, typed=False)
使用memoize包裝函數(shù),保存最近maxsize次的函數(shù)調(diào)用結(jié)果。在I/O-bound函數(shù)上裝飾,處理相同參數(shù)時(shí)可以節(jié)省函數(shù)執(zhí)行時(shí)間。
如果maxsize為None,會(huì)禁用LRU緩存特性(并非禁用緩存)且緩存大小會(huì)無(wú)限增長(zhǎng)。maxsize設(shè)置為2的n次方時(shí)性能最優(yōu)。
如果typed為T(mén)rue,不同類型函數(shù)參數(shù)的執(zhí)行結(jié)果會(huì)被分別緩存,例如f(3)和f(3.0)會(huì)被視為有兩個(gè)不同結(jié)果的不同調(diào)用。
因?yàn)樵撗b飾器使用字典緩存函數(shù)執(zhí)行結(jié)果,所以函數(shù)的位置參數(shù)和關(guān)鍵字參數(shù)必須是可哈希的。
不同的參數(shù)模式可能會(huì)被視為不同的緩存實(shí)體。例如f(a=1, b=2)和f(b=2, a=1)雖然只是關(guān)鍵字順序不同但可能有兩個(gè)多帶帶的緩存實(shí)體。
被包裝的函數(shù)可以調(diào)用cache_info(),它返回一個(gè)(hits, misses, maxsize, currsize)形式的命名元組;可以調(diào)用cache_clear()清空或使緩存無(wú)效;還可以調(diào)用__wrapped__屬性繞過(guò)緩存,訪問(wèn)原始的底層函數(shù)。
LRU(least recently used)緩存通常應(yīng)僅用在需要重用先前計(jì)算的值的場(chǎng)景,其他場(chǎng)景如使用LRU有不良影響、每次調(diào)用需要返回不同結(jié)果、time()、random()等應(yīng)禁止使用。maxsize表示緩存大小限制,確保不會(huì)無(wú)限制增長(zhǎng)。
@lru_cache(maxsize=32) def get_pep(num): "Retrieve text of a Python Enhancement Proposal" resource = "http://www.python.org/dev/peps/pep-%04d/" % num try: with urllib.request.urlopen(resource) as s: return s.read() except urllib.error.HTTPError: return "Not Found" >>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991: ... pep = get_pep(n) ... print(n, len(pep)) >>> get_pep.cache_info() CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)@functools.total_ordering
類裝飾器,包裝在定義了一個(gè)或多個(gè)富比較排序方法的類上,會(huì)補(bǔ)足其他的比較方法。
該類必須定義__lt__(), __le__(), __gt__(), 或__ge__()中至少一個(gè)方法,并建議定義__eq__()方法。
@total_ordering class Student: def __init__(self, lastname, firstname): self.lastname = lastname self.firstname = firstname def _is_valid_operand(self, other): return (hasattr(other, "lastname") and hasattr(other, "firstname")) def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.lastname.lower(), self.firstname.lower()) == (other.lastname.lower(), other.firstname.lower())) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.lastname.lower(), self.firstname.lower()) < (other.lastname.lower(), other.firstname.lower()))
NOTE:使用這個(gè)裝飾器的代價(jià)是,裝飾器自動(dòng)補(bǔ)足的比較方法耗費(fèi)了更多的執(zhí)行時(shí)間并擁有更復(fù)雜的堆棧跟蹤。如果應(yīng)用性能基準(zhǔn)測(cè)試表明是使用此裝飾器造成的瓶頸,手動(dòng)實(shí)現(xiàn)所有六種富比較方法可以帶來(lái)直觀的速度提升。
functools.partial(func, *args, **keywords)偏函數(shù),返回一個(gè)partial對(duì)象,調(diào)用時(shí)等同調(diào)用了使用預(yù)設(shè)位置參數(shù)和關(guān)鍵字參數(shù)的func函數(shù)。如果partial對(duì)象調(diào)用時(shí)又提供了額外的位置參數(shù),追加到args,如果提供了額外的關(guān)鍵字參數(shù),擴(kuò)展keywords并覆蓋相同的鍵值。
大致等同于:
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
partial()用于"凍結(jié)"函數(shù)的部分位置參數(shù)和/或關(guān)鍵字參數(shù)而產(chǎn)生一個(gè)代表某部分函數(shù)功能的簡(jiǎn)化標(biāo)志。
>>> from functools import partial >>> basetwo = partial(int, base=2) >>> basetwo.__doc__ = "Convert base 2 string to an int." >>> basetwo("10010") 18class functools.partialmethod(func, *args, **keywords)
后續(xù)補(bǔ)充
functools.reduce(function, iterable[, initializer])將可迭代對(duì)象iterable中的元素從左到右累計(jì)到接收兩個(gè)參數(shù)的函數(shù)func中。例如reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])的計(jì)算等同于((((1+2)+3)+4)+5)。
如果設(shè)置了可選參數(shù)initializer,它被放置在要計(jì)算的序列之前,并在序列為空時(shí)作為默認(rèn)值;如果未提供initializer的值且序列僅包含一個(gè)元素,返回該元素。
將函數(shù)轉(zhuǎn)換為單分派泛函數(shù)(single-dispatch generic function)。
使用@singledispatch裝飾器定義一個(gè)泛函數(shù),單分派僅限于第一個(gè)參數(shù)的類型:
from functools import singledispatch @singledispatch def fun(arg, verbose=False): if verbose: print("Let me just say,", end=" ") print(arg)
使用泛函數(shù)的register()屬性添加重載實(shí)現(xiàn),該屬性是一個(gè)裝飾器。對(duì)于使用類型注解的函數(shù),該裝飾器將自動(dòng)推斷第一個(gè)參數(shù)的類型:
@fun.register def _(arg: int, verbose=False): if verbose: print("Strength in numbers, eh?", end=" ") print(arg) @fun.register def _(arg: list, verbose=False): if verbose: print("Enumerate this:") for i, elem in enumerate(arg): print(i, elem)
如果不使用類型注解,可以顯式地給裝飾器傳遞類型參數(shù):
@fun.register(complex) def _(arg, verbose=False): if verbose: print("Better than complicated.", end=" ") print(arg.real, arg.imag)
也可以以函數(shù)的形式使用register()注冊(cè)lambda函數(shù)和已定義的函數(shù):
def nothing(arg, verbose=False): print("Nothing.") fun.register(type(None), nothing)
register()屬性返回允許裝飾器堆疊、序列化以及創(chuàng)建獨(dú)立的單元測(cè)試的未裝飾的函數(shù):
from decimal import Decimal @fun.register(float) @fun.register(Decimal) def fun_num(arg, verbose=False): if verbose: print("Half of your number:", end=" ") print(arg / 2) >>> fun_num is fun False
調(diào)用時(shí),泛函數(shù)只分派第一個(gè)參數(shù)的類型:
>>> fun("Hello, world.") Hello, world. >>> fun("test.", verbose=True) Let me just say, test. >>> fun(42, verbose=True) Strength in numbers, eh? 42 >>> fun(["spam", "spam", "eggs", "spam"], verbose=True) Enumerate this: 0 spam 1 spam 2 eggs 3 spam >>> fun(None) Nothing. >>> fun(1.23) 0.615
如果某個(gè)類型沒(méi)有相應(yīng)的實(shí)現(xiàn),將查找更通用的實(shí)現(xiàn)進(jìn)行解析。使用@singledispatch裝飾的原始函數(shù)注冊(cè)為object類型,將在沒(méi)有更好實(shí)現(xiàn)的情況下使用。調(diào)用dispatch()屬性查看泛函數(shù)使用了哪個(gè)實(shí)現(xiàn):
>>> fun.dispatch(float)>>> fun.dispatch(dict) # note: default implementation
調(diào)用registry屬性查看注冊(cè)的所有實(shí)現(xiàn):
>>> fun.registry.keys() dict_keys([functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES), , , , , ]) >>> fun.registry[float] >>> fun.registry[object]
更新包裝函數(shù)(wrapper),使其看起來(lái)更像被包裝的原始函數(shù)(wrapped)。元組類型的可選參數(shù)assigned指定wrapped函數(shù)的哪些屬性直接分派到wrapper函數(shù)對(duì)應(yīng)的屬性上,updated參數(shù)指定使用wrapped函數(shù)的哪些屬性更新wrapper函數(shù)對(duì)應(yīng)的屬性。
WRAPPER_ASSIGNMENTS的默認(rèn)值是"__module__", "__name__", "__qualname__", "__doc__","__annotations__"
WRAPPER_UPDATES的默認(rèn)值是"__dict__"
通過(guò)訪問(wèn)包裝函數(shù)的__wrapped__屬性可以獲取到被包裝的原始函數(shù)。
該函數(shù)主要用于裝飾器使用場(chǎng)景下,如果不更新包裝函數(shù),返回函數(shù)的元數(shù)據(jù)將指向包裝函數(shù)而非被包裝的原始函數(shù),一般來(lái)說(shuō)沒(méi)太大意義。
def wrapper(f): def wrapper_function(*args, **kwargs): """this is a wrapper function""" return f(*args, **kwargs) # update_wrapper(wrapper_function, f) return wrapper_function @wrapper def wrapped(): """this is a wrapped function""" pass print(wrapped.__doc__) print(wrapped.__name__)
# 不使用update_wrapper的結(jié)果: >>> this is a wrapper function >>> wrapper_function # 使用update_wrapper的結(jié)果: >>> this is a wrapped function >>> wrapped@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
等同于partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated),不過(guò)是作為包裝函數(shù)定義時(shí)的裝飾器使用。
def wrapper(f): @wraps(f) def wrapper_function(*args, **kwargs): """this is a wrapper function""" return f(*args, **kwargs) return wrapper_function @wrapper def wrapped(): """this is a wrapped function""" pass print(wrapped.__doc__) print(wrapped.__name__)partial對(duì)象
partial對(duì)象是由partial()方法創(chuàng)建的可調(diào)用對(duì)象,它有三個(gè)只讀屬性:
partial.func
一個(gè)可調(diào)用對(duì)象或函數(shù)。調(diào)用partial對(duì)象將使用新的位置參數(shù)和關(guān)鍵字參數(shù)轉(zhuǎn)發(fā)到func。
partial.args
調(diào)用partial()時(shí)提供的位置參數(shù)
partial.keywords
調(diào)用partial()時(shí)提供的關(guān)鍵字參數(shù)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/45040.html
摘要:函數(shù)內(nèi)省的內(nèi)容到此結(jié)束。函數(shù)式編程并不是一個(gè)函數(shù)式編程語(yǔ)言,但通過(guò)和等包的支持,也可以寫(xiě)出函數(shù)式風(fēng)格的代碼。 《流暢的Python》筆記。本篇主要講述Python中函數(shù)的進(jìn)階內(nèi)容。包括函數(shù)和對(duì)象的關(guān)系,函數(shù)內(nèi)省,Python中的函數(shù)式編程。 1. 前言 本片首先介紹函數(shù)和對(duì)象的關(guān)系;隨后介紹函數(shù)和可調(diào)用對(duì)象的關(guān)系,以及函數(shù)內(nèi)省。函數(shù)內(nèi)省這部分會(huì)涉及很多與IDE和框架相關(guān)的東西,如果平時(shí)...
摘要:變量查找規(guī)則在中一個(gè)變量的查找順序是局部環(huán)境,閉包,全局,內(nèi)建閉包引用了自由變量的函數(shù)。閉包的作用閉包的最大特點(diǎn)是可以將父函數(shù)的變量與內(nèi)部函數(shù)綁定,并返回綁定變量后的函數(shù),此時(shí)即便生成閉包的環(huán)境父函數(shù)已經(jīng)釋放,閉包仍然存在。 導(dǎo)語(yǔ):本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之函數(shù)篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門(mén)Python的朋友們可以來(lái)一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、掌握裝飾器的本質(zhì)、功...
摘要:看一個(gè)簡(jiǎn)單的例子在實(shí)際實(shí)驗(yàn)中,加不加并沒(méi)有區(qū)別。僅作了解這是個(gè)有趣的裝飾器,傳入的參數(shù)被打上了,當(dāng)下一次傳入的參數(shù)是一樣的時(shí)候,就會(huì)從中直接取出對(duì)應(yīng)的值,而不需要進(jìn)行重新的運(yùn)算。這樣做的好處是可以幫助我們分離代碼邏輯輸出 functools functools 包含了用于創(chuàng)建裝飾函數(shù),啟動(dòng)面向切面的編程,超出面向?qū)ο缶幊谭秶拇a復(fù)用,同時(shí)提供了裝飾函數(shù)用于豐富的快捷比較的API, p...
摘要:本文重點(diǎn)了解函數(shù)在中是一等對(duì)象了解中的可調(diào)用對(duì)象掌握正確定義函數(shù)參數(shù)的方法了解和中支持函數(shù)式編程的方法。歸約函數(shù)定義能夠接受一個(gè)可迭代對(duì)象并返回單個(gè)結(jié)果的函數(shù)是歸約函數(shù)。 導(dǎo)語(yǔ):本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之函數(shù)篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門(mén)Python的朋友們可以來(lái)一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、了解函數(shù)在Python中是一等對(duì)象;2、了解Python中的可調(diào)用對(duì)象;3...
摘要:本文重點(diǎn)了解函數(shù)在中是一等對(duì)象了解中的可調(diào)用對(duì)象掌握正確定義函數(shù)參數(shù)的方法了解和中支持函數(shù)式編程的方法。歸約函數(shù)定義能夠接受一個(gè)可迭代對(duì)象并返回單個(gè)結(jié)果的函數(shù)是歸約函數(shù)。 本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之函數(shù)篇的重點(diǎn)知識(shí)及個(gè)人心得,歡迎打算入門(mén)Python的朋友與我一起學(xué)習(xí)交流。。 本文重點(diǎn): 1、了解函數(shù)在Python中是一等對(duì)象;2、了解Python中的可調(diào)用對(duì)象;3、掌握...
閱讀 2465·2021-11-22 09:34
閱讀 3072·2021-10-25 09:43
閱讀 1987·2021-10-11 10:59
閱讀 3398·2021-09-22 15:13
閱讀 2334·2021-09-04 16:40
閱讀 426·2019-08-30 15:53
閱讀 3196·2019-08-30 11:13
閱讀 2610·2019-08-29 17:30