functools 可調用對象上的高階函數和操作

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__,namequalnameannotations 和__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在類中定義的對象的行爲類似於靜態方法,並且在實例屬性查找期間不會轉換爲綁定方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章