10.2. functools
- 可调用对象的高阶函数和操作¶
源代码: Lib / functools.py
functools
模块用于高阶函数:作用于或返回其他函数的函数。一般来说,对于这个模块,任何可调用对象都可以被视为函数。
functools
模块定义了以下函数︰
functools.
cmp_to_key
(func)¶将旧风格的比较函数转换为key函数。用于接受key函数的工具(例如
sorted()
,min()
,max()
,heapq.nlargest )
,heapq.nsmallest()
,itertools.groupby()
)。此函数主要用作从支持使用比较函数的Python 2转换的程序的过渡工具。比较函数是任何一个可调用的函数,且包含两个参数,对参数进行比较,如果小于返回负数,等于返回0,大于返回正数。Key函数是一个可调用对象,它接受一个参数并返回另一个值作为排序的键。
示例:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
有关排序示例和简要排序教程,请参阅排序HOWTO。
版本3.2中的新功能。
@
functools.
lru_cache
(maxsize=128, typed=False)¶装饰器用一个memoizing可调用函数来包装一个函数,它可以保存最近的maxsize个调用。当使用相同的参数定期调用昂贵的或I / O绑定的函数时,可以节省时间。
由于字典用于缓存结果,函数的位置和关键字参数必须是可哈希的。
如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是二的幂时,LRU功能执行得最好。
如果类型设置为True,则不同类型的函数参数将单独缓存。例如,
f(3)
和f(3.0)
将被视为具有不同结果的不同调用。To help measure the effectiveness of the cache and tune the maxsize parameter, the wrapped function is instrumented with a
cache_info()
function that returns a named tuple showing hits, misses, maxsize and currsize. 在多线程环境中,命中和未命中是近似的。装饰器还提供了用于清除或使缓存无效的
cache_clear()
函数。原始的底层函数可以通过
__wrapped__
属性访问。这对于内省,绕过缓存或者用不同的缓存重新封装函数很有用。当最近的呼叫是即将来电的最佳预测因子时,LRU(最近最少使用)高速缓存效果最好(例如,新闻服务器上最受欢迎的文章往往每天都更改)。缓存的大小限制确保缓存不会在长时间运行的进程(如Web服务器)上不受限制地增长。
用于静态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)
版本3.2中的新功能。
在版本3.3中更改:添加了类型选项。
@
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.2中的新功能。
在版本3.4中更改:现在支持从无法识别的类型的基础比较函数返回未实现。
functools.
partial
(func, *args, **keywords)¶返回一个新的
partial
对象,该对象在调用时将采用位置参数args和关键字参数关键字调用的func 。如果提供多个参数调用, 它们会被追加给 args。如果提供额外的关键字参数, 它们会扩展和覆盖 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()
用于部分函数应用程序,其“冻结”函数的参数和/或关键字的某些部分,从而产生具有简化声明的新对象。例如,partial()
可用于创建一个类似于int()
函数的可调用,其中base参数默认为两个:>>> 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必须是descriptor或可调用对象(这两个对象都像常规函数一样被处理为描述器)。
When func is a descriptor (such as a normal Python function,
classmethod()
,staticmethod()
,abstractmethod()
or another instance ofpartialmethod
), calls to__get__
are delegated to the underlying descriptor, and an appropriatepartial
object returned as the result.当func是非描述符可调用时,将动态创建适当的绑定方法。在作为方法使用时,它的行为类似于普通的Python函数:self参数将被插入作为第一个位置参数,甚至在args和t2>提供给
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
版本3.4中的新功能。
functools.
reduce
(function, iterable[, initializer])¶将两个参数的函数累加到序列的项中,从左到右,以便将序列减少为单个值。For example,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
calculates((((1+2)+3)+4)+5)
. 左参数,x是累加值,右参数,y是来自序列的更新值。如果存在可选的初始化器,则它将放置在计算中序列的项目之前,并在序列为空时用作默认值。如果未提供初始化程序且序列只包含一个项目,则返回第一个项目。大致相当于:
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
(default)¶ 将函数转换为single-dispatch generic function。
要定义通用函数,请使用
@singledispatch
装饰器进行装饰。注意,分派发生在第一个参数的类型上,相应地创建你的函数:>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg)
要向函数添加重载的实现,请使用通用函数的
register()
属性。它是一个装饰器,接受一个类型参数和装饰实现该类型的操作的函数:>>> @fun.register(int) ... def _(arg, verbose=False): ... if verbose: ... print("Strength in numbers, eh?", end=" ") ... print(arg) ... >>> @fun.register(list) ... def _(arg, verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
要启用注册lambda和预先存在的函数,可以以函数形式使用
register()
属性:>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)
register()
属性返回未装饰函数(原函数),该函数使装饰器堆叠,pickle化(序列化)以及为每个变体独立创建单元测试:>>> @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>
版本3.4中的新功能。
functools.
update_wrapper
(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶更新一个 wrapper 函数让其更像一个 wrapped 函数。可选的参数是一个元祖,来指定原函数哪些属性被直接分配给装饰器中的匹配属性 ,哪些装饰器属性使用来自原函数的相应属性来更新。The default values for these arguments are the module level constants
WRAPPER_ASSIGNMENTS
(which assigns to the wrapper function’s__module__
,__name__
,__qualname__
,__annotations__
and__doc__
, the documentation string) andWRAPPER_UPDATES
(which updates the wrapper function’s__dict__
, i.e. 实例的字典).为了允许访问原始功能以进行内省和其他目的(例如,绕过缓存装饰器(例如
lru_cache()
),此函数会自动向包装器添加一个__wrapped__
属性,该属性引用要包装的函数。此函数的主要用途是在decorator函数中,它包装修饰的函数并返回包装器。如果不更新包装器函数,返回的函数的元数据将反射包装器的定义,而不是原函数的定义 ,这通常是没有意义的。
update_wrapper()
可以与除函数之外的可调用项一起使用。忽略分配的或更新中指定的任何属性,这些属性从正在包装的对象中丢失。此函数不会尝试在包装函数中设置它们)。如果包装函数本身缺少在更新的中命名的任何属性,则仍会引发AttributeError
。版本3.2中的新功能:自动添加
__wrapped__
属性。版本3.2中的新功能:默认情况下复制
__annotations__
属性。在版本3.2中更改:缺少的属性不再触发
AttributeError
。在版本3.4中更改:
__wrapped__
属性现在总是引用wrapped函数,即使该函数定义了__wrapped__
属性。(参见问题17482)
@
functools.
wraps
(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶这是一个方便的函数,用于在定义包装器函数时调用
update_wrapper()
作为函数装饰器。它等效于partial(update_wrapper, wrapped = wrapped, assigned = assigned, updated = updated) t4 >
。示例:>>> 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()
的docstring将丢失。