functools
---可调用对象的高阶函数和操作¶
源代码: Lib/functools.py
这个 functools
模块用于高阶函数:作用于或返回其他函数的函数。一般来说,任何可调用对象都可以被视为用于此模块的函数。
这个 functools
模块定义以下功能:
- @functools.cache(user_function)¶
简单的轻量级无边界函数缓存。有时被称为 "memoize" .
返回与
lru_cache(maxsize=None)
,在函数参数的字典查找周围创建薄包装。因为它不需要逐出旧值,所以比lru_cache()
有大小限制。例如::
@cache def factorial(n): return n * factorial(n-1) if n else 1 >>> factorial(10) # no previously cached result, makes 11 recursive calls 3628800 >>> factorial(5) # just looks up cached value result 120 >>> factorial(12) # makes two new recursive calls, the other 10 are cached 479001600
3.9 新版功能.
- @functools.cached_property(func)¶
将类的方法转换为一个属性,该属性的值只计算一次,然后作为实例生命周期的普通属性进行缓存。类似
property()
,添加了缓存。对于实际不可变的实例的昂贵计算属性很有用。例子::
class DataSet: def __init__(self, sequence_of_numbers): self._data = tuple(sequence_of_numbers) @cached_property def stdev(self): return statistics.stdev(self._data)
它的力学原理
cached_property()
有点不同于property()
。除非定义了setter,否则常规属性会阻止属性写入。相比之下,一个 cached_property 允许写入。这个 cached_property Decorator仅在查找时运行,并且仅在同名属性不存在时运行。当它确实运行时, cached_property 写入同名的属性。后续属性读取和写入优先于 cached_property 方法,并且它的工作方式类似于普通属性。
可以通过删除属性来清除缓存值。这允许 cached_property 方法重新运行。
请注意,此修饰符会干扰 PEP 412 密钥共享字典。这意味着实例字典可能会比平时占用更多空间。
此外,该装饰器还要求
__dict__
属性是可变映射。这意味着它不能与某些类型一起工作,比如元类(因为__dict__
类型实例上的属性是类命名空间的只读代理),以及指定__slots__
不包括__dict__
作为定义的槽之一(因为这样的类不提供__dict__
属性)。如果可变映射不可用,或者需要节省空间的密钥共享,则会产生类似于
cached_property()
可以通过堆叠来实现property()
在……顶端cache()
::class DataSet: def __init__(self, sequence_of_numbers): self._data = sequence_of_numbers @property @cache def stdev(self): return statistics.stdev(self._data)
3.8 新版功能.
- functools.cmp_to_key(func)¶
将旧样式比较函数转换为 key function .与接受关键功能的工具(例如
sorted()
,min()
,max()
,heapq.nlargest()
,heapq.nsmallest()
,itertools.groupby()
)此函数主要用作从支持使用比较函数的python 2转换的程序的转换工具。比较函数是接受两个参数、比较它们并返回小于的负数、等于的零或大于的正数的任何可调用函数。键函数是接受一个参数并返回另一个值作为排序键的可调用函数。
例子::
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
有关排序示例和简短的排序教程,请参见 如何排序 .
3.2 新版功能.
- @functools.lru_cache(user_function)¶
- @functools.lru_cache(maxsize=128, typed=False)
decorator用一个memoizing可调用文件封装一个函数 最大尺寸 最近的调用。当使用相同的参数定期调用昂贵的或I/O绑定的函数时,可以节省时间。
由于字典用于缓存结果,因此函数的位置参数和关键字参数必须是可hash的。
不同的参数模式可以被认为是具有单独缓存项的不同调用。例如, f(a=1, b=2) 和 f(b=2, a=1) 关键字参数顺序不同,可能有两个单独的缓存项。
如果 user_function 指定了,它必须是可调用的。这使得 lru_cache 将直接应用于用户函数的decorator,保留 最大尺寸 默认值为128::
@lru_cache def count_vowels(sentence): sentence = sentence.casefold() return sum(sentence.count(vowel) for vowel in 'aeiou')
如果 最大尺寸 设置为
None
,LRU功能被禁用,缓存可以在不绑定的情况下增长。如果 类型化的 如果设置为true,则将分别缓存不同类型的函数参数。例如,
f(3)
和f(3.0)
将被视为具有不同结果的不同调用。包装的函数使用
cache_parameters()
返回新的dict
显示的值 最大尺寸 和 类型化的 . 仅供参考。改变这些值没有效果。帮助测量缓存的有效性并调整 最大尺寸 参数,封装函数使用
cache_info()
返回一个 named tuple 显示 hits , 思念 , 最大尺寸 和 咖喱尺码 . 在多线程环境中,命中和未命中是近似值。装饰器还提供
cache_clear()
用于清除或使缓存无效的函数。可以通过
__wrapped__
属性。这对于自省、绕过缓存或用不同的缓存重新封装函数非常有用。安 LRU (least recently used) cache 当最近的通话是即将到来的通话的最佳预测因素时(例如,新闻服务器上最流行的文章往往每天都在变化),效果最佳。缓存的大小限制确保缓存不会在没有绑定到长时间运行的进程(如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)
高效计算实例 Fibonacci numbers 使用缓存实现 dynamic programming 技术:
@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 版更改: 增加了 类型化的 选择权。
在 3.8 版更改: 增加了 user_function 选择权。
3.9 新版功能: 添加了函数
cache_parameters()
- @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()))
注解
虽然这个修饰器可以很容易地创建行为良好、完全有序的类型,但是 does 对于派生的比较方法,以较慢的执行速度和更复杂的堆栈跟踪为代价。如果性能基准测试表明这是给定应用程序的瓶颈,那么实现所有六种丰富的比较方法可能会提供一个简单的速度引发。
注解
此修饰符不尝试重写已在类中声明的方法 或其超类 。这意味着如果超类定义了比较运算符, total_ordering 将不会再次实现它,即使原始方法是抽象的。
3.2 新版功能.
在 3.4 版更改: 对于未识别的类型,现在支持从基础比较函数返回NotImplemented。
- functools.partial(func, /, *args, **keywords)¶
返回一个新的 partial object 调用时会表现得像 func 用位置参数调用 args 和关键字参数 关键词 . 如果向调用提供了更多参数,则这些参数将附加到 args . 如果提供了其他关键字参数,它们将扩展并重写 关键词 . 大致相当于:
def partial(func, /, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = {**keywords, **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 或者一个可调用的(像普通函数一样,两个对象都作为描述符处理)。
什么时候? func 是一个描述符(例如普通的python函数,
classmethod()
,staticmethod()
,abstractmethod()
或其他实例partialmethod
)__get__
被委托给基础描述符,并且 partial object 作为结果返回。什么时候? func 是非描述符可调用的,将动态创建适当的绑定方法。当用作方法时,它的行为类似于普通的python函数: self 参数将作为第一个位置参数插入,甚至在 args 和 关键词 提供给
partialmethod
构造函数。例子::
>>> 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.alive False >>> c.set_alive() >>> c.alive True
3.4 新版功能.
- functools.reduce(function, iterable[, initializer])¶
应用 function 两个参数的总和 可迭代的 从左到右,以便将ITerable减少到单个值。例如,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
计算((((1+2)+3)+4)+5)
.左边的参数, x ,是累积值和正确的参数, y ,是来自 可迭代的 . 如果可选 初始化器 如果存在,则将其放在计算中ITerable的项之前,并在ITerable为空时作为默认值。如果 初始化器 没有给予和 可迭代的 只包含一个项目,返回第一个项目。大致相当于:
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
见
itertools.accumulate()
对于生成所有中间值的迭代器。
- @functools.singledispatch¶
将函数转换为 single-dispatch generic function .
要定义通用函数,请用
@singledispatch
decorator。注意,调度发生在第一个参数的类型上,请相应地创建函数:>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg)
要向函数添加重载实现,请使用
register()
泛型函数的属性。它是一个装饰工。对于用类型注释的函数,decorator将自动推断第一个参数的类型::>>> @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
类型,这意味着如果找不到更好的实现,则使用它。如果实现注册到 abstract base class ,虚拟子类将被调度到该实现:
>>> 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
要检查泛型函数将为给定类型选择哪个实现,请使用
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 新版功能.
在 3.7 版更改: 这个
register()
属性支持使用类型批注。
- class functools.singledispatchmethod(func)¶
将方法转换为 single-dispatch generic function .
要定义通用方法,请用
@singledispatchmethod
decorator。注意,调度发生在第一个非self或非cls参数的类型上,请相应地创建函数:class Negator: @singledispatchmethod def neg(self, arg): raise NotImplementedError("Cannot negate a") @neg.register def _(self, arg: int): return -arg @neg.register def _(self, arg: bool): return not arg
@singledispatchmethod
支持与其他装饰器嵌套,例如@classmethod
. 注意考虑到dispatcher.register
,singledispatchmethod
必须是 最外面的 decorator。这里是Negator
类与neg
类绑定的方法:class Negator: @singledispatchmethod @classmethod def neg(cls, arg): raise NotImplementedError("Cannot negate a") @neg.register @classmethod def _(cls, arg: int): return -arg @neg.register @classmethod def _(cls, arg: bool): return not arg
同样的图案也可用于其他类似的装饰:
staticmethod
,abstractmethod
以及其他。3.8 新版功能.
- functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶
更新A 封装器 函数看起来像 包裹 功能。可选参数是元组,用于指定将原始函数的哪些属性直接分配给封装函数上的匹配属性,以及使用原始函数的相应属性更新封装函数的哪些属性。这些参数的默认值是模块级常量。
WRAPPER_ASSIGNMENTS
(分配给封装函数的__module__
,__name__
,__qualname__
,__annotations__
和__doc__
,文档字符串)和WRAPPER_UPDATES
(更新封装函数的__dict__
即实例字典)。允许访问原始函数以进行自省和其他目的(例如,绕过缓存修饰符,例如
lru_cache()
,此函数自动添加__wrapped__
属性指向引用要封装的函数的封装器。此功能的主要预期用途是 decorator 封装修饰函数并返回封装的函数。如果不更新封装函数,则返回函数的元数据将反映封装定义,而不是原始函数定义,这通常不太有用。
update_wrapper()
可以与函数以外的可调用文件一起使用。中命名的任何属性 分配 或 更新 被封装的对象中缺少的将被忽略(即,此函数不会尝试在封装函数上设置它们)。AttributeError
如果封装函数本身缺少 更新 .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
对象就像 function
对象,因为它们是可调用的、弱可引用的,并且可以具有属性。有一些重要的区别。例如, __name__
和 __doc__
属性不是自动创建的。也, partial
类中定义的对象的行为类似于静态方法,在实例属性查找期间不转换为绑定方法。