新聞中心
functools —- 高階函數(shù)和可調(diào)用對象上的操作
源代碼: Lib/functools.py

創(chuàng)新互聯(lián)-云計算及IDC服務(wù)提供商,涵蓋公有云、IDC機(jī)房租用、服務(wù)器托管、等保安全、私有云建設(shè)等企業(yè)級互聯(lián)網(wǎng)基礎(chǔ)服務(wù),咨詢熱線:18980820575
functools 模塊應(yīng)用于高階函數(shù),即參數(shù)或(和)返回值為其他函數(shù)的函數(shù)。 通常來說,此模塊的功能適用于所有可調(diào)用對象。
functools 模塊定義了以下函數(shù):
@functools.cache(user_function)
簡單輕量級未綁定函數(shù)緩存。 有時稱為 “memoize”。
返回值與 lru_cache(maxsize=None) 相同,創(chuàng)建一個查找函數(shù)參數(shù)的字典的簡單包裝器。 因?yàn)樗恍枰瞥雠f值,所以比帶有大小限制的 lru_cache() 更小更快。
例如:
@cachedef factorial(n):return n * factorial(n-1) if n else 1>>> factorial(10) # no previously cached result, makes 11 recursive calls3628800>>> factorial(5) # just looks up cached value result120>>> factorial(12) # makes two new recursive calls, the other 10 are cached479001600
3.9 新版功能.
@functools.cached_property(func)
將一個類方法轉(zhuǎn)換為特征屬性,一次性計算該特征屬性的值,然后將其緩存為實(shí)例生命周期內(nèi)的普通屬性。 類似于 property() 但增加了緩存功能。 對于在其他情況下實(shí)際不可變的高計算資源消耗的實(shí)例特征屬性來說該函數(shù)非常有用。
示例:
class DataSet:def __init__(self, sequence_of_numbers):self._data = tuple(sequence_of_numbers)@cached_propertydef stdev(self):return statistics.stdev(self._data)
cached_property() 的設(shè)定與 property() 有所不同。 常規(guī)的 property 會阻止屬性寫入,除非定義了 setter。 與之相反,cached_property 則允許寫入。
cached_property 裝飾器僅在執(zhí)行查找且不存在同名屬性時才會運(yùn)行。 當(dāng)運(yùn)行時,cached_property 會寫入同名的屬性。 后續(xù)的屬性讀取和寫入操作會優(yōu)先于 cached_property 方法,其行為就像普通的屬性一樣。
緩存的值可通過刪除該屬性來清空。 這允許 cached_property 方法再次運(yùn)行。
注意,這個裝飾器會影響 PEP 412 鍵共享字典的操作。 這意味著相應(yīng)的字典實(shí)例可能占用比通常時更多的空間。
而且,這個裝飾器要求每個實(shí)例上的 __dict__ 是可變的映射。 這意味著它將不適用于某些類型,例如元類(因?yàn)轭愋蛯?shí)例上的 __dict__ 屬性是類命名空間的只讀代理),以及那些指定了 __slots__ 但未包括 __dict__ 作為所定義的空位之一的類(因?yàn)檫@樣的類根本沒有提供 __dict__ 屬性)。
如果可變的映射不可用或者如果想要節(jié)省空間的鍵共享,可以通過在 cache() 之上堆疊一個 property() 來實(shí)現(xiàn)類似 cached_property() 的效果:
class DataSet:def __init__(self, sequence_of_numbers):self._data = sequence_of_numbers@property@cachedef stdev(self):return statistics.stdev(self._data)
3.8 新版功能.
functools.cmp_to_key(func)
將(舊式的)比較函數(shù)轉(zhuǎn)換為新式的 key function . 在類似于 sorted() , min() , max() , heapq.nlargest() , heapq.nsmallest() , itertools.groupby() 等函數(shù)的 key 參數(shù)中使用。此函數(shù)主要用作將 python 2 程序轉(zhuǎn)換至新版的轉(zhuǎn)換工具,以保持對比較函數(shù)的兼容。
A comparison function is any callable that accepts two arguments, compares them, and returns a negative number for less-than, zero for equality, or a positive number for greater-than. A key function is a callable that accepts one argument and returns another value to be used as the sort key.
示例:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
有關(guān)排序示例和簡要排序教程,請參閱 排序指南 。
3.2 新版功能.
@functools.lru_cache(user_function)
@functools.lru_cache(maxsize=128, typed=False)
一個為函數(shù)提供緩存功能的裝飾器,緩存 maxsize 組傳入?yún)?shù),在下次以相同參數(shù)調(diào)用時直接返回上一次的結(jié)果。用以節(jié)約高開銷或I/O函數(shù)的調(diào)用時間。
由于使用了字典存儲緩存,所以該函數(shù)的固定參數(shù)和關(guān)鍵字參數(shù)必須是可哈希的。
Distinct argument patterns may be considered to be distinct calls with separate cache entries. For example, f(a=1, b=2) and f(b=2, a=1) differ in their keyword argument order and may have two separate cache entries.
如果指定了 user_function,它必須是一個可調(diào)用對象。 這允許 lru_cache 裝飾器被直接應(yīng)用于一個用戶自定義函數(shù),讓 maxsize 保持其默認(rèn)值 128:
@lru_cachedef count_vowels(sentence):return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')
如果 maxsize 設(shè)為 None,LRU 特性將被禁用且緩存可無限增長。
如果 typed 被設(shè)置為 true ,不同類型的函數(shù)參數(shù)將被分別緩存。 如果 typed 為 false ,實(shí)現(xiàn)通常會將它們視為等價的調(diào)用,只緩存一個結(jié)果。(有些類型,如 str 和 int ,即使 typed 為 false ,也可能被分開緩存)。
注意,類型的特殊性只適用于函數(shù)的直接參數(shù)而不是它們的內(nèi)容。 標(biāo)量參數(shù) Decimal(42) 和 Fraction(42) 被視為具有不同結(jié)果的不同調(diào)用。相比之下,元組參數(shù) ('answer', Decimal(42)) 和 ('answer', Fraction(42)) 被視為等同的。
被包裝的函數(shù)配有一個 cache_parameters() 函數(shù),該函數(shù)返回一個新的 dict 用來顯示 maxsize 和 typed 的值。 這只是出于顯示信息的目的。 改變值沒有任何效果。
為了幫助衡量緩存的有效性以及調(diào)整 maxsize 形參,被包裝的函數(shù)會帶有一個 cache_info() 函數(shù),它返回一個 named tuple 以顯示 hits, misses, maxsize 和 currsize。
該裝飾器也提供了一個用于清理/使緩存失效的函數(shù) cache_clear() 。
原始的未經(jīng)裝飾的函數(shù)可以通過 __wrapped__ 屬性訪問。它可以用于檢查、繞過緩存,或使用不同的緩存再次裝飾原始函數(shù)。
緩存會保持對參數(shù)的引用并返回值,直到它們結(jié)束生命期退出緩存或者直到緩存被清空。
If a method is cached, the self instance argument is included in the cache. See 我該如何緩存方法調(diào)用?
LRU(最久未使用算法)緩存) 在最近的調(diào)用是即將到來的調(diào)用的最佳預(yù)測值時性能最好(例如,新聞服務(wù)器上最熱門文章傾向于每天更改)。 緩存的大小限制可確保緩存不會在長期運(yùn)行進(jìn)程如網(wǎng)站服務(wù)器上無限制地增長。
一般來說,LRU緩存只在當(dāng)你想要重用之前計算的結(jié)果時使用。因此,用它緩存具有副作用的函數(shù)、需要在每次調(diào)用時創(chuàng)建不同、易變的對象的函數(shù)或者諸如time()或random()之類的不純函數(shù)是沒有意義的。
靜態(tài) Web 內(nèi)容的 LRU 緩存示例:
@lru_cache(maxsize=32)def get_pep(num):'Retrieve text of a Python Enhancement Proposal'resource = 'https://peps.python.org/pep-%04d/' % numtry: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)
以下是使用緩存通過 動態(tài)規(guī)劃 計算 斐波那契數(shù)列 的例子。
@lru_cache(maxsize=None)def fib(n):if n < 2:return nreturn fib(n-1) + fib(n-2)>>> [fib(n) for n in range(16)][0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]>>> fib.cache_info()CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
3.2 新版功能.
在 3.3 版更改: 添加 typed 選項(xiàng)。
在 3.8 版更改: 添加了 user_function 選項(xiàng)。
3.9 新版功能: 新增函數(shù) cache_parameters()
@functools.total_ordering
給定一個聲明一個或多個全比較排序方法的類,這個類裝飾器實(shí)現(xiàn)剩余的方法。這減輕了指定所有可能的全比較操作的工作。
此類必須包含以下方法之一:__lt__() 、__le__()、__gt__() 或 __ge__()。另外,此類必須支持 __eq__() 方法。
例如:
@total_orderingclass Student:def _is_valid_operand(self, other):return (hasattr(other, "lastname") andhasattr(other, "firstname"))def __eq__(self, other):if not self._is_valid_operand(other):return NotImplementedreturn ((self.lastname.lower(), self.firstname.lower()) ==(other.lastname.lower(), other.firstname.lower()))def __lt__(self, other):if not self._is_valid_operand(other):return NotImplementedreturn ((self.lastname.lower(), self.firstname.lower()) <(other.lastname.lower(), other.firstname.lower()))
備注
雖然此裝飾器使得創(chuàng)建具有良好行為的完全有序類型變得非常容易,但它 確實(shí) 是以執(zhí)行速度更緩慢和派生比較方法的堆?;厮莞鼜?fù)雜為代價的。 如果性能基準(zhǔn)測試表明這是特定應(yīng)用的瓶頸所在,則改為實(shí)現(xiàn)全部六個富比較方法應(yīng)該會輕松提升速度。
備注
這個裝飾器不會嘗試重載類 或其上級類 中已經(jīng)被聲明的方法。 這意味著如果某個上級類定義了比較運(yùn)算符,則 total_ordering 將不會再次實(shí)現(xiàn)它,即使原方法是抽象方法。
3.2 新版功能.
在 3.4 版更改: 現(xiàn)在已支持從未識別類型的下層比較函數(shù)返回 NotImplemented 異常。
functools.partial(func, /, \args, **keywords*)
返回一個新的 部分對象,當(dāng)被調(diào)用時其行為類似于 func 附帶位置參數(shù) args 和關(guān)鍵字參數(shù) keywords 被調(diào)用。 如果為調(diào)用提供了更多的參數(shù),它們會被附加到 args。 如果提供了額外的關(guān)鍵字參數(shù),它們會擴(kuò)展并重載 keywords。 大致等價于:
def partial(func, /, *args, **keywords):def newfunc(*fargs, **fkeywords):newkeywords = {**keywords, **fkeywords}return func(*args, *fargs, **newkeywords)newfunc.func = funcnewfunc.args = argsnewfunc.keywords = keywordsreturn newfunc
partial() 會被“凍結(jié)了”一部分函數(shù)參數(shù)和/或關(guān)鍵字的部分函數(shù)應(yīng)用所使用,從而得到一個具有簡化簽名的新對象。 例如,partial() 可用來創(chuàng)建一個行為類似于 int() 函數(shù)的可調(diào)用對象,其中 base 參數(shù)默認(rèn)為二:
>>> from functools import partial>>> basetwo = partial(int, base=2)>>> basetwo.__doc__ = 'Convert base 2 string to an int.'>>> basetwo('10010')18
class functools.partialmethod(func, /, \args, **keywords*)
返回一個新的 partialmethod 描述器,其行為類似 partial 但它被設(shè)計用作方法定義而非直接用作可調(diào)用對象。
func 必須是一個 descriptor 或可調(diào)用對象(同屬兩者的對象例如普通函數(shù)會被當(dāng)作描述器來處理)。
當(dāng) func 是一個描述器(例如普通 Python 函數(shù), classmethod(), staticmethod(), abstractmethod() 或其他 partialmethod 的實(shí)例)時, 對 __get__ 的調(diào)用會被委托給底層的描述器,并會返回一個適當(dāng)?shù)?部分對象 作為結(jié)果。
當(dāng) func 是一個非描述器類可調(diào)用對象時,則會動態(tài)創(chuàng)建一個適當(dāng)?shù)慕壎ǚ椒ā?當(dāng)用作方法時其行為類似普通 Python 函數(shù):將會插入 self 參數(shù)作為第一個位置參數(shù),其位置甚至?xí)幱谔峁┙o partialmethod 構(gòu)造器的 args 和 keywords 之前。
示例:
>>> class Cell:... def __init__(self):... self._alive = False... @property... def alive(self):... return self._alive... def set_state(self, state):... self._alive = bool(state)... set_alive = partialmethod(set_state, True)... set_dead = partialmethod(set_state, False)...>>> c = Cell()>>> c.aliveFalse>>> c.set_alive()>>> c.aliveTrue
3.4 新版功能.
functools.reduce(function, iterable[, initializer])
將兩個參數(shù)的 function 從左至右積累地應(yīng)用到 iterable 的條目,以便將該可迭代對象縮減為單一的值。 例如,reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 是計算 ((((1+2)+3)+4)+5) 的值。 左邊的參數(shù) x 是積累值而右邊的參數(shù) y 則是來自 iterable 的更新值。 如果存在可選項(xiàng) initializer,它會被放在參與計算的可迭代對象的條目之前,并在可迭代對象為空時作為默認(rèn)值。 如果沒有給出 initializer 并且 iterable 僅包含一個條目,則將返回第一項(xiàng)。
大致相當(dāng)于:
def reduce(function, iterable, initializer=None):it = iter(iterable)if initializer is None:value = next(it)else:value = initializerfor element in it:value = function(value, element)return value
請參閱 itertools.accumulate() 了解有關(guān)可產(chǎn)生所有中間值的迭代器。
@functools.singledispatch
將一個函數(shù)轉(zhuǎn)換為 單分派 generic function。
要定義一個泛型函數(shù),用裝飾器 @singledispatch 來裝飾它。當(dāng)使用 @singledispatch 定義一個函數(shù)時,請注意調(diào)度發(fā)生在第一個參數(shù)的類型上:
>>> from functools import singledispatch>>> @singledispatch... def fun(arg, verbose=False):... if verbose:... print("Let me just say,", end=" ")... print(arg)
要將重載的實(shí)現(xiàn)添加到函數(shù)中,請使用泛型函數(shù)的 register() 屬性,它可以被用作裝飾器。 對于帶有類型標(biāo)注的函數(shù),該裝飾器將自動推斷第一個參數(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)
types.UnionType and typing.Union can also be used:
>>> @fun.register... def _(arg: int | float, verbose=False):... if verbose:... print("Strength in numbers, eh?", end=" ")... print(arg)...>>> from typing import Union>>> @fun.register... def _(arg: Union[list, set], verbose=False):... if verbose:... print("Enumerate this:")... for i, elem in enumerate(arg):... print(i, elem)...
對于不使用類型標(biāo)注的代碼,可以將適當(dāng)?shù)念愋蛥?shù)顯式地傳給裝飾器本身:
>>> @fun.register(complex)... def _(arg, verbose=False):... if verbose:... print("Better than complicated.", end=" ")... print(arg.real, arg.imag)...
要啟用注冊 lambda 和現(xiàn)有的函數(shù),也可以使用 register() 屬性的函數(shù)形式:
>>> def nothing(arg, verbose=False):... print("Nothing.")...>>> fun.register(type(None), nothing)
register() 屬性會返回未被裝飾的函數(shù)。 這將啟用裝飾器棧、封存,并為每個變量單獨(dú)創(chuàng)建單元測試:
>>> @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 funFalse
在調(diào)用時,泛型函數(shù)會根據(jù)第一個參數(shù)的類型進(jìn)行分派:
>>> 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 spam1 spam2 eggs3 spam>>> fun(None)Nothing.>>> fun(1.23)0.615
在沒有針對特定類型的已注冊實(shí)現(xiàn)的情況下,會使用其方法解析順序來查找更通用的實(shí)現(xiàn)。 使用 @singledispatch 裝飾的原始函數(shù)將為基本的 object 類型進(jìn)行注冊,這意味著它將在找不到更好的實(shí)現(xiàn)時被使用。
如果一個實(shí)現(xiàn)被注冊到 abstract base class,則基類的虛擬子類將被發(fā)送到該實(shí)現(xiàn):
>>> from collections.abc import Mapping>>> @fun.register... def _(arg: Mapping, verbose=False):... if verbose:... print("Keys & Values")... for key, value in arg.items():... print(key, "=>", value)...>>> fun({"a": "b"})a => b
要檢查泛型函數(shù)將為給定的類型選擇哪個實(shí)現(xiàn),請使用 dispatch() 屬性:
>>> fun.dispatch(float)>>> fun.dispatch(dict) # note: default implementation
要訪問所有已注冊實(shí)現(xiàn),請使用只讀的 registry 屬性:
>>> fun.registry.keys()dict_keys([, , , , , ]) >>> fun.registry[float]>>> fun.registry[object]
3.4 新版功能.
在 3.7 版更改: register() 屬性現(xiàn)在支持使用類型標(biāo)注。
在 3.11 版更改: The register() attribute now supports types.UnionType and typing.Union as type annotations.
class functools.singledispatchmethod(func)
將一個方法轉(zhuǎn)換為 單分派 generic function。
要定義一個泛型方法,請用 @singledispatchmethod 裝飾器來裝飾它。 當(dāng)使用 @singledispatchmethod 定義一個函數(shù)時,請注意發(fā)送操作將針對第一個非 self 或非 cls 參數(shù)的類型上:
class Negator:@singledispatchmethoddef neg(self, arg):raise NotImplementedError("Cannot negate a")@neg.registerdef _(self, arg: int):return -arg@neg.registerdef _(self, arg: bool):return not arg
@singledispatchmethod 支持與其他裝飾器如 @classmethod 相嵌套。 請注意為了允許 dispatcher.register,singledispatchmethod 必須是 最外層的 裝飾器。 下面是一個 Negator 類包含綁定到類的 neg 方法,而不是一個類實(shí)例:
class Negator:@singledispatchmethod@classmethoddef neg(cls, arg):raise NotImplementedError("Cannot negate a")@neg.register@classmethoddef _(cls, arg: int):return -arg@neg.register@classmethoddef _(cls, arg: bool):return not arg
同樣的模式也可被用于其他類似的裝飾器: @staticmethod, @abstractmethod 等等。
3.8 新版功能.
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
更新一個 wrapper 函數(shù)以使其類似于 wrapped 函數(shù)。 可選參數(shù)為指明原函數(shù)的哪些屬性要直接被賦值給 wrapper 函數(shù)的匹配屬性的元組,并且這些 wrapper 函數(shù)的屬性將使用原函數(shù)的對應(yīng)屬性來更新。 這些參數(shù)的默認(rèn)值是模塊級常量 WRAPPER_ASSIGNMENTS (它將被賦值給 wrapper 函數(shù)的 __module__, __name__, __qualname__, __annotations__ 和 __doc__ 即文檔字符串) 以及 WRAPPER_UPDATES (它將更新 wrapper 函數(shù)的 __dict__ 即實(shí)例字典)。
為了允許出于內(nèi)省和其他目的訪問原始函數(shù)(例如繞過 lru_cache() 之類的緩存裝飾器),此函數(shù)會自動為 wrapper 添加一個指向被包裝函數(shù)的 __wrapped__ 屬性。
此函數(shù)的主要目的是在 decorator 函數(shù)中用來包裝被裝飾的函數(shù)并返回包裝器。 如果包裝器函數(shù)未被更新,則被返回函數(shù)的元數(shù)據(jù)將反映包裝器定義而不是原始函數(shù)定義,這通常沒有什么用處。
update_wrapper() 可以與函數(shù)之外的可調(diào)用對象一同使用。 在 assigned 或 updated 中命名的任何屬性如果不存在于被包裝對象則會被忽略(即該函數(shù)將不會嘗試在包裝器函數(shù)上設(shè)置它們)。 如果包裝器函數(shù)自身缺少在 updated 中命名的任何屬性則仍將引發(fā) AttributeError。
3.2 新版功能: 自動添加 __wrapped__ 屬性。
3.2 新版功能: 默認(rèn)拷貝 __annotations__ 屬性。
在 3.2 版更改: 不存在的屬性將不再觸發(fā) AttributeError。
在 3.4 版更改: __wrapped__ 屬性現(xiàn)在總是指向被包裝的函數(shù),即使該函數(shù)定義了 __wrapped__ 屬性。 (參見 bpo-17482)
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
這是一個便捷函數(shù),用于在定義包裝器函數(shù)時發(fā)起調(diào)用 update_wrapper() 作為函數(shù)裝飾器。 它等價于 partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)。 例如:
>>> from functools import wraps>>> def my_decorator(f):... @wraps(f)... def wrapper(*args, **kwds):... print('Calling decorated function')... return f(*args, **kwds)... return wrapper...>>> @my_decorator... def example():... """Docstring"""... print('Called example function')...>>> example()Calling decorated functionCalled example function>>> example.__name__'example'>>> example.__doc__'Docstring'
如果不使用這個裝飾器工廠函數(shù),則 example 函數(shù)的名稱將變?yōu)?'wrapper',并且 example() 原本的文檔字符串將會丟失。
partial 對象
partial 對象是由 partial() 創(chuàng)建的可調(diào)用對象。 它們具有三個只讀屬性:
partial.func
一個可調(diào)用對象或函數(shù)。 對 partial 對象的調(diào)用將被轉(zhuǎn)發(fā)給 func 并附帶新的參數(shù)和關(guān)鍵字。
partial.args
最左邊的位置參數(shù)將放置在提供給 partial 對象調(diào)用的位置參數(shù)之前。
partial.keywords
當(dāng)調(diào)用 partial 對象時將要提供的關(guān)鍵字參數(shù)。
partial 對象與 function 對象的類似之處在于它們都是可調(diào)用、可弱引用的對象并可擁有屬性。 但兩者也存在一些重要的區(qū)別。 例如前者不會自動創(chuàng)建 __name__ 和 __doc__ 屬性。 而且,在類中定義的 partial 對象的行為類似于靜態(tài)方法,并且不會在實(shí)例屬性查找期間轉(zhuǎn)換為綁定方法。
新聞標(biāo)題:創(chuàng)新互聯(lián)Python教程:functools—-高階函數(shù)和可調(diào)用對象上的操作
網(wǎng)站鏈接:http://fisionsoft.com.cn/article/cojsoie.html


咨詢
建站咨詢
