functools — Функции высшего порядка и операции над вызываемыми объектами
Исходный код: Lib/functools.py
Модуль functools предназначен для функций высшего порядка: функций, которые действуют над другими функциями или возвращают их. В целом, любой вызываемый объект может рассматриваться как функция для целей этого модуля.
Модуль functools определяет следующие функции:
-
@functools.cache(user_function) -
Простая, легкая, неограниченная кэш-память функций. Иногда называется “мемоизацией”.
Возвращает то же, что и
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(). Обычное свойство блокирует запись в атрибут, если не определен метод установки. В отличие от этого, cached_property разрешает записи.Декоратор cached_property выполняется только при обращении и только когда атрибут с тем же именем не существует. Когда он выполняется, 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) -
Преобразует функцию сравнения старого стиля в функцию ключа функцию ключа. Используется с инструментами, которые принимают функции ключа (такими как
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) -
Декоратор для обертывания функции вызываемым объектом с кешированием, сохраняющим до maxsize последних вызовов. Он может экономить время, когда дорогостоящая или привязанная к вводу/выводу функция периодически вызывается с одними и теми же аргументами.
Поскольку для кеширования используются словари, позиционные и ключевые аргументы функции должны быть хешируемыми.
Разные шаблоны аргументов могут считаться разными вызовами с отдельными записями в кеше. Например,
f(a=1, b=2)иf(b=2, a=1)отличаются порядком ключевых аргументов и могут иметь две отдельные записи в кеше.Если user_function указано, то это должен быть вызываемый объект. Это позволяет применить декоратор lru_cache напрямую к пользовательской функции, оставив maxsize по умолчанию в 128:
@lru_cache def count_vowels(sentence): sentence = sentence.casefold() return sum(sentence.count(vowel) for vowel in 'aeiou')Если maxsize установлено в
None, функция LRU отключена, и кеш может расти без ограничений.Если typed установлено в истину, аргументы функции разных типов будут кешироваться отдельно. Например,
f(3)иf(3.0)будут обрабатываться как различные вызовы с различными результатами.Обернутая функция снабжена функцией
cache_parameters(), которая возвращает новыйdict, показывающий значения для maxsize и typed. Это для информационных целей только. Изменение значений не имеет эффекта.Для измерения эффективности кеша и настройки параметра maxsize обернутая функция снабжена функцией
cache_info(), которая возвращает именованную кортеж, показывающий hits, misses, maxsize и currsize. В многопоточной среде значения попаданий и промахов являются приблизительными.Декоратор также предоставляет функцию
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 = 'https://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.
Изменено в версии 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()))Примечание
Хотя этот декоратор делает создание хорошо себя ведущих полностью упорядоченных типов лёгким, это понижает скорость выполнения и усложняет трассировки стека для выведенных методов сравнения. Если по результатам бенчмаркинга производительности выяснится, что это узкое место для данного приложения, то, вероятно, реализация всех шести методов богатого сравнения вместо него обеспечит повышение скорости.
Введено в версии 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, **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: ... 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 слева направо, чтобы свести iterable к одному значению. Например,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])вычисляет((((1+2)+3)+4)+5). Левый аргумент x — это накопленное значение, а правый аргумент y — значение обновления из iterable. Если присутствует необязательный initializer, он помещается перед элементами iterable в вычислении и служит значением по умолчанию, когда iterable пустой. Если initializer не задан, а 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 -
Преобразует функцию в одноточечную обобщённую функцию.
Для определения обобщённой функции используйте декоратор
@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) ...Для возможности регистрации лямбда-выражений и предварительно существующих функций, атрибут
register()также может быть использован в функциональной форме:>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)Атрибут
register()возвращает функцию без декоратора. Это позволяет использовать стекирование декораторов,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При вызове обобщённая функция выполняет распределение по типу первого аргумента:
>>> 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, что означает её использование, если не найдена лучшая реализация.Если реализация зарегистрирована для абстрактного базового класса, виртуальные подклассы базового класса будут распределены к этой реализации:
>>> 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) -
Преобразует метод в одноточечную обобщённую функцию.
Для определения обобщённого метода используйте декоратор
@singledispatchmethod. При определении функции с помощью@singledispatchmethod, обратите внимание, что распределение происходит по типу первого аргумента, не являющегося 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должен быть внешним декоратором. Вот класс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) -
Обновить функцию-обёртку, чтобы она выглядела как обёрнутая функция. Дополнительные аргументы — кортежи, указывающие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки и какие атрибуты функции-обёртки обновляются соответствующими атрибутами исходной функции. Значения по умолчанию для этих аргументов — константы уровня модуля
WRAPPER_ASSIGNMENTS(которые присваивают функции-обёртке__module__,__name__,__qualname__,__annotations__и__doc__, строку документации) иWRAPPER_UPDATES(которые обновляют словарь экземпляра функции-обёртки__dict__)).Для доступа к исходной функции с целью интроспекции и других целей (например, для обхода кеширующего декоратора, такого как
lru_cache()), эта функция автоматически добавляет атрибут__wrapped__к обёртке, который ссылается на обёрнутую функцию.Основное назначение этой функции — в функциях-декораторах, которые обертывают декорируемую функцию и возвращают обёртку. Если функция-обёртка не обновлена, метаданные возвращаемой функции будут отражать определение функции-обёртки, а не исходной функции, что обычно нежелательно.
update_wrapper()может использоваться с вызываемыми объектами, отличными от функций. Любые атрибуты, указанные в assigned или updated, отсутствующие в обёрнутом объекте, игнорируются (т. е. эта функция не попытается установить их в функции-обёртке).AttributeErrorвсё ещё генерируется, если у самой функции-обёртки отсутствуют атрибуты, указанные в updated.New in version 3.2: Автоматическое добавление атрибута
__wrapped__.New in version 3.2: Копирование атрибута
__annotations__по умолчанию.Changed in version 3.2: Отсутствующие атрибуты больше не вызывают
AttributeError.Changed in version 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, определённые в классах, ведут себя как статические методы и не преобразуются в связанные методы во время поиска атрибутов экземпляра.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/library/functools.html