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()
. Обычное свойство блокирует запись атрибутов, если не определен сеттер. В отличие от него, 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)¶ Преобразуйте функцию сравнения старого стиля в 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) Декоратор, позволяющий обернуть функцию мемоизирующей вызываемой функцией, которая сохраняет до maxsize последних вызовов. Это может сэкономить время, когда дорогая или связанная с вводом/выводом функция периодически вызывается с одними и теми же аргументами.
Поскольку для кэширования результатов используется словарь, позиционные и ключевые аргументы функции должны быть хэшируемыми.
Различные шаблоны аргументов могут рассматриваться как отдельные вызовы с отдельными записями в кэше. Например, f(a=1, b=2) и f(b=2, a=1) отличаются порядком аргументов ключевых слов и могут иметь две отдельные записи в кэше.
Если указана user_function, она должна быть вызываемой. Это позволяет применить декоратор lru_cache непосредственно к пользовательской функции, оставив maxsize по умолчанию равным 128:
@lru_cache def count_vowels(sentence): return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')
Если maxsize установлен в
None
, функция LRU отключена, и кэш может расти неограниченно.Если typed имеет значение true, аргументы функций разных типов будут кэшироваться отдельно. Если typed равно false, реализация обычно рассматривает их как эквивалентные вызовы и кэширует только один результат. (Некоторые типы, такие как str и int, могут кэшироваться отдельно даже при значении typed false).
Обратите внимание, что спецификация типов применяется только к непосредственным аргументам функции, а не к их содержимому. Скалярные аргументы
Decimal(42)
иFraction(42)
рассматриваются как разные вызовы с разными результатами. Напротив, аргументы кортежа('answer', Decimal(42))
и('answer', Fraction(42))
рассматриваются как эквивалентные.Обернутая функция оснащена функцией
cache_parameters()
, которая возвращает новуюdict
, показывающую значения для maxsize и typed. Это только для информационных целей. Изменение значений не имеет никакого эффекта.Чтобы помочь измерить эффективность кэша и настроить параметр maxsize, обернутая функция оснащена функцией
cache_info()
, которая возвращает named tuple, показывая hits, misses, maxsize и currsize.Декоратор также предоставляет функцию
cache_clear()
для очистки или аннулирования кэша.Оригинальная базовая функция доступна через атрибут
__wrapped__
. Это полезно для интроспекции, для обхода кэша или для повторного обертывания функции с другим кэшем.Кэш сохраняет ссылки на аргументы и возвращаемые значения до тех пор, пока они не выйдут из кэша или пока кэш не будет очищен.
LRU (least recently used) cache лучше всего работает, когда последние обращения являются лучшими предсказателями предстоящих обращений (например, самые популярные статьи на сервере новостей имеют тенденцию меняться каждый день). Ограничение размера кэша гарантирует, что кэш не будет неограниченно расти на длительно работающих процессах, таких как веб-серверы.
В целом, кэш 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)
Пример эффективного вычисления 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: Добавлена опция 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()))
Примечание
Хотя этот декоратор позволяет легко создавать хорошо управляемые полностью упорядоченные типы, он достается ценой более медленного выполнения и более сложных стековых трассировок для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для данного приложения, то реализация всех шести богатых методов сравнения вместо этого, вероятно, обеспечит легкое увеличение скорости.
Примечание
Этот декоратор не пытается переопределить методы, которые были объявлены в классе или его суперклассах. Это означает, что если суперкласс определяет оператор сравнения, total_ordering не будет реализовывать его снова, даже если исходный метод является абстрактным.
Добавлено в версии 3.2.
Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из базовой функции сравнения для нераспознанных типов.
-
functools.
partial
(func, /, *args, **keywords)¶ Возвращает новый partial object, который при вызове будет вести себя как 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 должен быть descriptor или callable (объекты, которые являются и тем, и другим, как обычные функции, обрабатываются как дескрипторы).
Когда func является дескриптором (например, обычной функцией Python,
classmethod()
,staticmethod()
,abstractmethod()
или другим экземпляромpartialmethod
), вызовы__get__
передаются базовому дескриптору, а в качестве результата возвращается соответствующий partial object.Когда 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])¶ Применить функцию из двух аргументов кумулятивно к элементам итерабельной таблицы, слева направо, так, чтобы свести итерабельную таблицу к одному значению. Например,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
вычисляет((((1+2)+3)+4)+5)
. Левый аргумент, x, является накопленным значением, а правый аргумент, y, является обновленным значением из iterable. Если присутствует необязательный initializer, он помещается перед элементами итерабельной таблицы в вычислении и служит по умолчанию, когда итерабельная таблица пуста. Если 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
¶ Преобразование функции в single-dispatch generic function.
Чтобы определить общую функцию, украсьте ее декоратором
@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) ...
Для возможности регистрации lambdas и уже существующих функций, атрибут
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
, что означает, что она используется, если не найдено лучшей реализации.Если реализация зарегистрирована в 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
. При определении функции с помощью@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__
, который ссылается на обертываемую функцию.Основное предполагаемое использование этой функции - в функциях decorator, которые оборачивают декорированную функцию и возвращают обертку. Если функция-обертка не обновлена, метаданные возвращаемой функции будут отражать определение обертки, а не исходное определение функции, что обычно менее полезно.
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'
, а docstring оригинальнойexample()
был бы потерян.
partial
Объекты¶
Объекты partial
- это вызываемые объекты, создаваемые partial()
. Они имеют три атрибута, доступных только для чтения:
-
partial.
func
¶ Вызываемый объект или функция. Вызовы к объекту
partial
будут перенаправлены вfunc
с новыми аргументами и ключевыми словами.
-
partial.
args
¶ Самые левые позиционные аргументы, которые будут добавлены к позиционным аргументам, предоставленным для вызова объекта
partial
.
Объекты partial
похожи на объекты function
тем, что они вызываемые, слабо ссылаемые и могут иметь атрибуты. Есть несколько важных различий. Например, атрибуты __name__
и __doc__
не создаются автоматически. Кроме того, объекты partial
, определенные в классах, ведут себя как статические методы и не превращаются в связанные методы при поиске атрибутов экземпляра.