functools
partial
itertools — 创建高效循环迭代器的函数
itertools
源代码: Lib/functools.py
The functools 模块用于高阶函数:充当 (或返回) 其它函数的函数。一般而言,任何可调用对象都可以视为用于此模块用途的函数。
The functools 模块定义了下列函数:
简单轻量的无界函数缓存。有时称为 “memoize” .
返回如同 lru_cache(maxsize=None) ,创建查找函数自变量的字典的瘦包裹器。因为从不需要驱逐旧值,这更小且更快相比 lru_cache() 带尺寸限制。
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
The cache is threadsafe so that the wrapped function can be used in multiple threads. This means that the underlying data structure will remain coherent during concurrent updates.
It is possible for the wrapped function to be called more than once if another thread makes an additional call before the initial call has been completed and cached.
Added in version 3.9.
将类方法变换成值计算一次,然后将值作为实例生命周期正常缓存属性的属性。类似于 property() , with the addition of caching. Useful for expensive computed properties of instances that are otherwise effectively immutable.
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)
The mechanics of cached_property() are somewhat different from property() . A regular property blocks attribute writes unless a setter is defined. In contrast, a cached_property allows writes.
cached_property()
The cached_property decorator only runs on lookups and only when an attribute of the same name doesn’t exist. When it does run, the cached_property writes to the attribute with the same name. Subsequent attribute reads and writes take precedence over the cached_property method and it works like a normal attribute.
The cached value can be cleared by deleting the attribute. This allows the cached_property method to run again.
The cached_property does not prevent a possible race condition in multi-threaded usage. The getter function could run more than once on the same instance, with the latest run setting the cached value. If the cached property is idempotent or otherwise not harmful to run more than once on an instance, this is fine. If synchronization is needed, implement the necessary locking inside the decorated getter function or around the cached property access.
Note, this decorator interferes with the operation of PEP 412 key-sharing dictionaries. This means that instance dictionaries can take more space than usual.
Also, this decorator requires that the __dict__ attribute on each instance be a mutable mapping. This means it will not work with some types, such as metaclasses (since the __dict__ attributes on type instances are read-only proxies for the class namespace), and those that specify __slots__ without including __dict__ as one of the defined slots (as such classes don’t provide a __dict__ attribute at all).
__dict__
__slots__
If a mutable mapping is not available or if space-efficient key sharing is desired, an effect similar to cached_property() can also be achieved by stacking property() on top of lru_cache() 。见 How do I cache method calls? for more details on how this differs from cached_property() .
Added in version 3.8.
Changed in version 3.12: Prior to Python 3.12, cached_property included an undocumented lock to ensure that in multi-threaded usage the getter function was guaranteed to run only once per instance. However, the lock was per-property, not per-instance, which could result in unacceptably high lock contention. In Python 3.12+ this locking is removed.
cached_property
Transform an old-style comparison function to a 关键函数 . Used with tools that accept key functions (such as sorted() , min() , max() , heapq.nlargest() , heapq.nsmallest() , itertools.groupby() ). This function is primarily used as a transition tool for programs being converted from Python 2 which supported the use of comparison functions.
sorted()
min()
max()
heapq.nlargest()
heapq.nsmallest()
itertools.groupby()
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
对于排序范例和简短排序教程,见 Sorting Techniques .
Added in version 3.2.
装饰器采用保存的记忆可调用来包裹函数,直到 maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.
Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable .
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.
f(a=1, b=2)
f(b=2, a=1)
若 user_function 有指定,它必须是可调用。这允许 lru_cache decorator to be applied directly to a user function, leaving the maxsize at its default value of 128:
@lru_cache def count_vowels(sentence): return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')
若 maxsize 被设为 None , the LRU feature is disabled and the cache can grow without bound.
None
若 typed is set to true, function arguments of different types will be cached separately. If typed is false, the implementation will usually regard them as equivalent calls and only cache a single result. (Some types such as str and int may be cached separately even when typed is false.)
Note, type specificity applies only to the function’s immediate arguments rather than their contents. The scalar arguments, Decimal(42) and Fraction(42) are be treated as distinct calls with distinct results. In contrast, the tuple arguments ('answer', Decimal(42)) and ('answer', Fraction(42)) are treated as equivalent.
Decimal(42)
Fraction(42)
('answer', Decimal(42))
('answer', Fraction(42))
The wrapped function is instrumented with a cache_parameters() function that returns a new dict showing the values for maxsize and typed . This is for information purposes only. Mutating the values has no effect.
cache_parameters()
dict
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 命名元组 showing hits , misses , maxsize and currsize .
cache_info()
装饰器还提供 cache_clear() 函数用于清零 (或使缓存无效)。
cache_clear()
The original underlying function is accessible through the __wrapped__ attribute. This is useful for introspection, for bypassing the cache, or for rewrapping the function with a different cache.
__wrapped__
The cache keeps references to the arguments and return values until they age out of the cache or until the cache is cleared.
If a method is cached, the self instance argument is included in the cache. See How do I cache method calls?
self
An LRU (least recently used) cache works best when the most recent calls are the best predictors of upcoming calls (for example, the most popular articles on a news server tend to change each day). The cache’s size limit assures that the cache does not grow without bound on long-running processes such as web servers.
In general, the LRU cache should only be used when you want to reuse previously computed values. Accordingly, it doesn’t make sense to cache functions with side-effects, functions that need to create distinct mutable objects on each call (such as generators and async functions), or impure functions such as time() or random().
Example of an LRU cache for static web content:
@lru_cache(maxsize=32) def get_pep(num): 'Retrieve text of a Python Enhancement Proposal' resource = f'https://peps.python.org/pep-{num:04d}' 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)
Example of efficiently computing 斐波那契数 using a cache to implement a 动态编程 技术:
@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.3 版改变: 添加 typed 选项。
3.8 版改变: 添加 user_function 选项。
3.9 版改变: 添加函数 cache_parameters()
给定定义一个或多个丰富比较次序方法的类,此类装饰器供给其余方法。这简化指定所有可能丰富比较操作涉及的努力:
类必须定义一个 __lt__() , __le__() , __gt__() ,或 __ge__() 。此外,类应提供 __eq__() 方法。
__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()))
注意
While this decorator makes it easy to create well behaved totally ordered types, it does come at the cost of slower execution and more complex stack traces for the derived comparison methods. If performance benchmarking indicates this is a bottleneck for a given application, implementing all six rich comparison methods instead is likely to provide an easy speed boost.
This decorator makes no attempt to override methods that have been declared in the class or its superclasses . Meaning that if a superclass defines a comparison operator, total_ordering will not implement it again, even if the original method is abstract.
3.4 版改变: Returning NotImplemented from the underlying comparison function for unrecognised types is now supported.
NotImplemented
返回新的 partial 对象 which when called will behave like func called with the positional arguments args 和关键词自变量 keywords . If more arguments are supplied to the call, they are appended to args . If additional keyword arguments are supplied, they extend and override keywords . Roughly equivalent to:
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
The partial() is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature. For example, partial() can be used to create a callable that behaves like the int() function where the base argument defaults to two:
partial()
int()
>>> from functools import partial >>> basetwo = partial(int, base=2) >>> basetwo.__doc__ = 'Convert base 2 string to an int.' >>> basetwo('10010') 18
返回新的 partialmethod 描述符其行为像 partial except that it is designed to be used as a method definition rather than being directly callable.
partialmethod
func 必须为 descriptor or a callable (objects which are both, like normal functions, are handled as descriptors).
当 func 是描述符 (譬如:正常 Python 函数, classmethod() , staticmethod() , abstractmethod() 或另一实例化的 partialmethod ),调用 __get__ are delegated to the underlying descriptor, and an appropriate partial 对象 returned as the result.
classmethod()
staticmethod()
abstractmethod()
__get__
当 func is a non-descriptor callable, an appropriate bound method is created dynamically. This behaves like a normal Python function when used as a method: the self argument will be inserted as the first positional argument, even before the args and keywords supplied to the 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
Added in version 3.4.
应用 function 的 2 自变量的累积到项为 iterable ,从左到右,以便将可迭代缩减到单个值。例如, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 计算 ((((1+2)+3)+4)+5) 。左自变量 x , is the accumulated value and the right argument, y , is the update value from the iterable . If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty. If initializer 未给定且 iterable contains only one item, the first item is returned.
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
见 itertools.accumulate() for an iterator that yields all intermediate values.
itertools.accumulate()
将函数变换成 单次分派 一般函数 .
To define a generic function, decorate it with the @singledispatch decorator. When defining a function using @singledispatch , note that the dispatch happens on the type of the first argument:
@singledispatch
>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg)
To add overloaded implementations to the function, use the register() attribute of the generic function, which can be used as a decorator. For functions annotated with types, the decorator will infer the type of the first argument automatically:
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)
types.UnionType and typing.Union can also be used:
types.UnionType
typing.Union
>>> @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) ...
For code which doesn’t use type annotations, the appropriate type argument can be passed explicitly to the decorator itself:
>>> @fun.register(complex) ... def _(arg, verbose=False): ... if verbose: ... print("Better than complicated.", end=" ") ... print(arg.real, arg.imag) ...
To enable registering lambdas and pre-existing functions, the register() attribute can also be used in a functional form:
>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)
The register() attribute returns the undecorated function. This enables decorator stacking, pickling , and the creation of unit tests for each variant independently:
pickling
>>> @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
When called, the generic function dispatches on the type of the first argument:
>>> 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
Where there is no registered implementation for a specific type, its method resolution order is used to find a more generic implementation. The original function decorated with @singledispatch is registered for the base object type, which means it is used if no better implementation is found.
object
If an implementation is registered to an 抽象基类 , virtual subclasses of the base class will be dispatched to that implementation:
>>> 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
To check which implementation the generic function will choose for a given type, use the dispatch() 属性:
dispatch()
>>> fun.dispatch(float) <function fun_num at 0x1035a2840> >>> fun.dispatch(dict) # note: default implementation <function fun at 0x103fe0000>
To access all registered implementations, use the read-only registry 属性:
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.7 版改变: The register() 属性现在支持使用类型注解。
3.11 版改变: The register() 属性现在支持 types.UnionType and typing.Union as type annotations.
把方法变换成 单次分派 一般函数 .
要定义一般方法,装饰它采用 @singledispatchmethod decorator. When defining a function using @singledispatchmethod , note that the dispatch happens on the type of the first non- self or non- cls 自变量:
@singledispatchmethod
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 supports nesting with other decorators such as @classmethod . Note that to allow for dispatcher.register , singledispatchmethod must be the outer most decorator. Here is the Negator 类采用 neg methods bound to the class, rather than an instance of the class:
@classmethod
dispatcher.register
singledispatchmethod
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
The same pattern can be used for other similar decorators: @staticmethod , @abstractmethod , and others.
@staticmethod
@abstractmethod
Update a wrapper function to look like the wrapped function. The optional arguments are tuples to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function and which attributes of the wrapper function are updated with the corresponding attributes from the original function. The default values for these arguments are the module level constants WRAPPER_ASSIGNMENTS (which assigns to the wrapper function’s __module__ , __name__ , __qualname__ , __annotations__ , __type_params__ ,和 __doc__ , the documentation string) and WRAPPER_UPDATES (which updates the wrapper function’s __dict__ , i.e. the instance dictionary).
WRAPPER_ASSIGNMENTS
__module__
__name__
__qualname__
__annotations__
__type_params__
__doc__
WRAPPER_UPDATES
To allow access to the original function for introspection and other purposes (e.g. bypassing a caching decorator such as lru_cache() ), this function automatically adds a __wrapped__ attribute to the wrapper that refers to the function being wrapped.
The main intended use for this function is in 装饰器 functions which wrap the decorated function and return the wrapper. If the wrapper function is not updated, the metadata of the returned function will reflect the wrapper definition rather than the original function definition, which is typically less than helpful.
update_wrapper() may be used with callables other than functions. Any attributes named in assigned or updated that are missing from the object being wrapped are ignored (i.e. this function will not attempt to set them on the wrapper function). AttributeError is still raised if the wrapper function itself is missing any attributes named in updated .
update_wrapper()
AttributeError
3.2 版改变: The __wrapped__ attribute is now automatically added. The __annotations__ attribute is now copied by default. Missing attributes no longer trigger an AttributeError .
3.4 版改变: The __wrapped__ attribute now always refers to the wrapped function, even if that function defined a __wrapped__ 属性。(见 bpo-17482 )
Changed in version 3.12: The __type_params__ attribute is now copied by default.
这是方便函数为援引 update_wrapper() 作为函数装饰器当定义包裹器函数时。它相当于 partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) 。例如:
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'
不使用此装饰器工厂,example 函数名称将拥有 'wrapper' ,且 docstring (文档字符串) 对于原始 example() 会丢失。
'wrapper'
example()
partial 对象是可调用对象创建通过 partial() 。它们有 3 个只读属性:
可调用对象或函数。调用 partial 对象会转发给 func 采用新自变量和关键词。
func
The leftmost positional arguments that will be prepended to the positional arguments provided to a partial object call.
The keyword arguments that will be supplied when the partial object is called.
partial 对象像 function objects in that they are callable, weak referenceable, and can have attributes. There are some important differences. For instance, the __name__ and __doc__ attributes are not created automatically. Also, partial objects defined in classes behave like static methods and do not transform into bound methods during instance attribute look-up.
function
operator — 作为函数的标准运算符
operator
键入搜索术语或模块、类、函数名称。