functools — Функции высшего порядка и операции над вызываемыми объектами
Исходный код: Lib/functools.py
Модуль 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
Примеры сортировки и краткий учебник по сортировке см. в Сортировка HOW TO.
Новая в версии 3.2.
-
@functools.lru_cache(maxsize=128, typed=False) -
Декоратор для обертывания функции вызываемым объектом кеширования, сохраняющим до maxsize последних вызовов. Это может экономить время, когда дорогостоящая или привязанная к вводу-выводу функция периодически вызывается с одними и теми же аргументами.
Поскольку для кеширования используются словари, позиционные и именованные аргументы функции должны быть хешируемыми.
Если maxsize установлено в
None, функция кеширования LRU отключена, и кеш может расти без ограничений. Функция кеширования LRU работает лучше всего, когда maxsize является степенью двойки.Если typed установлено в true, функции аргументов разных типов будут кешироваться отдельно. Например,
f(3)иf(3.0)будут рассматриваться как отдельные вызовы с отдельными результатами.Для измерения эффективности кеша и настройки параметра maxsize обернутая функция снабжена функцией
cache_info(), которая возвращает именованную кортеж, отображающий hits, misses, maxsize и currsize. В многопоточной среде значения hits и misses являются приблизительными.Декоратор также предоставляет функцию
cache_clear()для очистки или аннулирования кеша.Исходная функция доступна через атрибут
__wrapped__. Это полезно для интроспекции, для обхода кеша или для повторной обертки функции с другим кешем.Кеш LRU (least recently used) работает лучше всего, когда самые последние вызовы являются лучшими предсказателями будущих вызовов (например, наиболее популярные статьи на новостном сервере, как правило, меняются каждый день). Ограничение размера кеша гарантирует, что кеш не будет расти без ограничений при длительных процессах, таких как веб-серверы.
В общем случае кеш LRU следует использовать только тогда, когда вы хотите повторно использовать ранее вычисленные значения. Соответственно, нет смысла кешировать функции с побочными эффектами, функции, которые должны создавать отдельные изменяемые объекты при каждом вызове, или нечистые функции, такие как time() или random().
Пример кеша 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: Добавлен параметр typed.
-
@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: Теперь поддерживается возвращение NotImplemented от функции сравнения для нераспознанных типов.
-
functools.partial(func, *args, **keywords) -
Возвращает новый объект partial, который при вызове будет вести себя так же, как func, вызванный с позиционными аргументами args и именованными аргументами keywords. Если в вызов передаются дополнительные аргументы, они добавляются к 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 должен быть дескриптором или вызываемым объектом (объекты, которые являются обоими, например, обычные функции, обрабатываются как дескрипторы).
Когда func является дескриптором (например, обычная Python-функция,
classmethod(),staticmethod(),abstractmethod()или другой экземплярpartialmethod), вызовы__get__делегируются основному дескриптору, и возвращается соответствующий объект partial.Когда func является вызываемым объектом, который не является дескриптором, динамически создается соответствующий связанный метод. Он ведет себя как обычная Python-функция при использовании в качестве метода: аргумент self будет вставлен в качестве первого позиционного аргумента, даже до args и keywords, переданных конструктору
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]) -
Применяет function с двумя аргументами кумулятивно к элементам sequence слева направо, чтобы свести последовательность к одному значению. Например,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])вычисляет((((1+2)+3)+4)+5). Левый аргумент, x, представляет накопленное значение, а правый аргумент, y, — обновляемое значение из sequence. Если присутствует необязательный initializer, он размещается перед элементами последовательности в вычислении и служит значением по умолчанию, когда последовательность пуста. Если initializer не задан, а sequence содержит только один элемент, возвращается этот первый элемент.Примерно эквивалентно:
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(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)Для возможности регистрации лямбда-выражений и уже существующих функций можно использовать атрибут
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>Новая в версии 3.4.
-
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) -
Обновляет функцию-обёртку (wrapper), чтобы она выглядела как функция-объект (wrapped). Необязательные аргументы — кортежи, определяющие, какие атрибуты оригинальной функции будут напрямую присвоены соответствующим атрибутам функции-обёртки, и какие атрибуты функции-обёртки будут обновлены соответствующими атрибутами оригинальной функции. Значения по умолчанию для этих аргументов — модульные константы
WRAPPER_ASSIGNMENTS(которые присваивают функции-обёртке__module__,__name__,__qualname__,__annotations__и__doc__, строку документации) иWRAPPER_UPDATES(которая обновляет словарь экземпляра функции-обёртки__dict__).Для возможности доступа к оригинальной функции для интроспекции и других целей (например, для обхода кэширующего декоратора, такого как
lru_cache()), эта функция автоматически добавляет атрибут__wrapped__в функцию-обёртку, который ссылается на функцию, которая оборачивается.Основное предназначение этой функции — в функциях-декораторах, которые оборачивают декорированную функцию и возвращают функцию-обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение функции-обёртки, а не определение оригинальной функции, что обычно нежелательно.
update_wrapper()может быть использована с вызовами, отличными от функций. Любые атрибуты, указанные в assigned или updated, которые отсутствуют в объекте, который оборачивается, игнорируются (то есть, эта функция не попытается установить их в функцию-обёртку).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()была бы потеряна.
10.2.1. Объекты partial
partial — это вызываемые объекты, созданные с помощью partial(). У них есть три только для чтения атрибута:
-
partial.func -
Вызываемый объект или функция. Вызовы объекта
partialбудут перенаправлены вfuncс новыми аргументами и ключевыми словами.
-
partial.args -
Позиционные аргументы, которые будут добавлены в начало позиционных аргументов, переданных вызову объекта
partial.
-
partial.keywords -
Ключевые аргументы, которые будут переданы при вызове объекта
partial.
partial объекты похожи на function объекты тем, что они вызываемые, слабые, и у них могут быть атрибуты. Существуют некоторые важные различия. Например, атрибуты __name__ и __doc__ не создаются автоматически. Также, объекты partial, определённые в классах, ведут себя как статические методы и не преобразуются в связанные методы во время поиска атрибутов экземпляра.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/functools.html