functools—可調用對象上的高階函數和操作
functools 該模塊用於高階函數:作用於或返回其他函數的函數。通常,就此模塊而言,任何可調用對象都可以視爲函數。
該模塊定義了以下功能:functools
functools.cmp_to_key(func )
將舊式比較功能轉換爲鍵功能。使用接受鑰匙功能的工具(如sorted(),min(), max(),heapq.nlargest(),heapq.nsmallest(), itertools.groupby())。該函數主要用作從Python 2轉換而來的程序的過渡工具,該程序支持使用比較函數。
比較函數是任何可調用的函數,它們接受兩個參數,進行比較,併爲小於小於返回一個負數,等於返回零,或者爲大於大於返回一個正數。鍵函數是一個可調用函數,它接受一個參數並返回另一個值用作排序鍵。
例:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
@functools.lru_cache(maxsize = 128,typed = False )
裝飾包裹可贖回一個memoizing,節省高達功能 MAXSIZE最近通話。當使用相同的參數定期調用昂貴的或I / O綁定的函數時,可以節省時間。
由於使用字典來緩存結果,因此該函數的位置和關鍵字參數必須是可哈希的。
可以將不同的參數模式視爲具有單獨的緩存條目的不同調用。例如,f(a = 1,b = 2)和f(b = 2,a = 1) 的關鍵字參數順序不同,並且可能具有兩個單獨的緩存條目。
如果將maxsize設置爲None,則將禁用LRU功能,並且緩存可以無限增長。當maxsize爲2的冪時,LRU功能表現最佳。
如果將typed設置爲true,則將分別緩存不同類型的函數參數。例如,f(3)和f(3.0)將被視爲具有不同結果的不同調用。
爲了幫助衡量高速緩存的有效性並調整maxsize 參數,包裝的函數將配備一個cache_info() 函數,該函數返回一個顯示命中,未命中, maxsize和currsize的命名元組。在多線程環境中,命中率和未命中率是近似的。
裝飾器還提供cache_clear()用於清除或使高速緩存無效的功能。
原始基礎功能可通過該__wrapped__屬性訪問 。這對於自省,繞過緩存或用其他緩存重新包裝功能很有用。
一個LRU(最近最少使用)緩存效果最好的最近通話是即將到來的呼叫的最佳預測(例如,新聞服務器上的最流行的文章往往每天更換)。緩存的大小限制可確保緩存不會在不受長時間運行的進程(例如Web服務器)的限制下增長。
通常,僅當您要重用先前計算的值時,才應使用LRU緩存。因此,緩存具有副作用的函數,需要在每次調用時創建不同的可變對象的函數或不純函數(例如time()或random())都是沒有意義的。
用於靜態Web內容的LRU緩存示例:
@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)
使用高速緩存有效地計算斐波那契數以實現 動態編程 技術的示例 :
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return 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)
@functools.total_ordering
給定一個定義了一個或多個豐富比較排序方法的類,此類裝飾器將提供其餘的類。這簡化了指定所有可能的豐富比較操作所涉及的工作:
這個類必須定義之一__lt__(),__le__(), __gt__(),或__ge__()。另外,該類應提供一個__eq__()方法。
例如:
@total_ordering
class Student:
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()))
注意 雖然這個裝飾可以很容易地創建很乖全序類型,它確實來得比較慢的執行和更復雜的堆棧跟蹤派生比較方法的成本。如果性能基準測試表明這是給定應用程序的瓶頸,則實施所有六種豐富的比較方法可能會輕鬆提高速度。
在版本3.4中進行了更改:現在支持從基礎比較函數爲無法識別的類型返回NotImplemented。
functools.partial(func,* args,** keywords )
返回一個新的局部對象,該局部對象在被調用時的行爲類似於使用位置參數args 和關鍵字arguments 關鍵字調用的func。如果將更多參數提供給調用,則將它們附加到args。如果有額外的關鍵字提供參數,他們擴展和覆蓋關鍵字。大致相當於:
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()用於局部功能應用其中“凍結”的函數的參數和/或產生具有簡化簽名的新對象的關鍵字一些部分。例如,partial()可用於創建行爲類似於int()函數的可調用對象,其中基本參數默認爲兩個:
>>> 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但該描述符被設計爲用作方法定義而不是直接可調用的。
func必須是描述符或可調用的(與正常功能一樣,都作爲描述符處理的對象)。
當FUNC是一個描述符(例如正常Python函數, classmethod(),staticmethod(),abstractmethod()或的另一個實例partialmethod),則調用__get__委派到底層描述符,和一個適當的 部分對象作爲結果返回。
當func是非描述符可調用的時,將動態創建適當的綁定方法。當用作方法時,其行爲類似於普通的Python函數:self參數將作爲第一個位置參數插入,甚至在提供給構造函數的args和關鍵字之前partialmethod。
例:
>>> class Cell(object):
... 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.alive
False
>>> c.set_alive()
>>> c.alive
True
functools.reduce(function, iterable[, initializer])
將兩個自變量的函數從左到右累計應用於序列項,以將序列減少爲單個值。例如, 計算。左邊的參數x是累加值,右邊的參數y是序列中的更新值。如果存在可選的初始化程序,則它將被放置在計算中序列項的前面,並在序列爲空時用作默認值。如果未給出初始值設定項,並且 序列僅包含一項,則返回第一項。reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])((((1+2)+3)+4)+5)
大致相當於:
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
@functools.singledispatch
將一個函數轉換爲一個單派發 通用函數。
要定義泛型函數,請使用裝飾器對其進行@singledispatch 裝飾。請注意,分派發生在第一個參數的類型上,請相應地創建函數:
>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
... if verbose:
... print("Let me just say,", end=" ")
... print(arg)
要將重載的實現添加到函數中,請使用register() 泛型函數的屬性。它是一個裝飾。對於帶有類型註釋的函數,裝飾器將自動推斷第一個參數的類型:
>>> @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)
對於不使用類型註釋的代碼,可以將適當的類型參數顯式傳遞給裝飾器本身:
>>> @fun.register(complex)
... def _(arg, verbose=False):
... if verbose:
... print("Better than complicated.", end=" ")
... print(arg.real, arg.imag)
...
要啓用註冊lambda和預先存在的功能,register()可以以功能形式使用該 屬性:
>>> def nothing(arg, verbose=False):
... print("Nothing.")
...
>>> fun.register(type(None), nothing)
該register()屬性返回未修飾的函數,該函數啓用裝飾器堆棧,酸洗以及爲每個變量獨立創建單元測試:
>>> @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
調用時,泛型函數分派第一個參數的類型:
>>> 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
如果沒有針對特定類型的註冊實現,則使用其方法解析順序來查找更通用的實現。裝飾@singledispatch有的原始功能已註冊爲基本object類型,這意味着如果找不到更好的實現,則使用該功能。
要檢查通用函數將爲給定類型選擇哪種實現,請使用dispatch()屬性:
>>> fun.dispatch(float)
<function fun_num at 0x1035a2840>
>>> fun.dispatch(dict) # note: default implementation
<function fun at 0x103fe0000>
要訪問所有已註冊的實現,請使用只讀registry 屬性:
>>> fun.registry.keys()
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
<class 'decimal.Decimal'>, <class 'list'>,
<class 'float'>])
>>> fun.registry[float]
<function fun_num at 0x1035a2840>
>>> fun.registry[object]
<function fun at 0x103fe0000>
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
更新包裝函數,使其看起來像包裝函數。可選參數是元組,用於指定將原始函數的哪些屬性直接分配給包裝函數上的匹配屬性,以及使用原始函數中的相應屬性更新包裝函數的哪些屬性。這些參數的默認值是模塊級的常量WRAPPER_ASSIGNMENTS(其中分配給包裝函數的__module__,name,qualname,annotations 和__doc__,文檔字符串)和WRAPPER_UPDATES(其更新的包裝函數的__dict__,即實例字典)。
爲了允許出於自省和其他目的訪問原始函數(例如,繞過諸如的緩存裝飾器lru_cache()),此函數自動__wrapped__向包裝器添加一個屬性,該屬性引用被包裝的函數。
此函數的主要用途是在裝飾函數中,該函數包裝已裝飾的函數並返回包裝器。如果包裝器函數未更新,則返回的函數的元數據將反映包裝器定義,而不是原始函數定義,這通常會有所幫助。
update_wrapper()可以與函數以外的可調用對象一起使用。被包裝的對象中缺少的在分配或更新中命名的任何屬性都將被忽略(即,此函數將不會嘗試在包裝函數上設置它們)。AttributeError如果包裝器函數本身缺少updated中命名的任何屬性,則仍會引發。
版本3.2中的新功能:自動添加__wrapped__屬性。
版本3.2中的新增功能:__annotations__默認情況下,屬性複製。
在版本3.2中更改:缺少屬性不再觸發AttributeError。
在版本3.4中進行了更改:__wrapped__現在,屬性始終引用包裝的函數,即使該函數定義了__wrapped__屬性也是如此。(請參閱bpo-17482)
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
這是一個方便的函數,用於update_wrapper()在定義包裝函數時調用爲函數裝飾器。等同於 。例如: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 function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
如果不使用此裝飾器工廠,則示例函數的名稱將爲’wrapper’,並且原始函數example() 的文檔字符串將丟失。
partial對象
partial對象是由創建的可調用對象partial()。它們具有三個只讀屬性:
partial.func
可調用的對象或函數。對partial對象的調用將func使用新的參數和關鍵字轉發到該對象。
partial.args
最左邊的位置參數將放在提供給partial對象調用的位置參數之前。
partial.keywords
partial調用對象時將提供的關鍵字參數。
partial對象就像function對象一樣,它們是可調用的,弱引用的,並且可以具有屬性。有一些重要的區別。例如,__name__和__doc__屬性不會自動創建。同樣,partial在類中定義的對象的行爲類似於靜態方法,並且在實例屬性查找期間不會轉換爲綁定方法。