functools.md
1> **Источник:** https://python-all.ru/3.11/library/functools.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`functools`](https://python-all.ru/3.11/library/functools.html#module-functools) – Функции высшего порядка и операции над вызываемыми объектами89**Исходный код:** [Lib/functools.py](https://python-all.ru/src/3.11/Lib/functools.py)1011---1213Модуль [`functools`](https://python-all.ru/3.11/library/functools.html#module-functools) предназначен для функций высшего порядка: функций, которые действуют на или возвращают другие функции. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.1415Модуль [`functools`](https://python-all.ru/3.11/library/functools.html#module-functools) определяет следующие функции:1617#### `@functools.cache(user_function)`1819Простой легковесный кэш функций без ограничения размера. Иногда называется [«мемоизация»](https://python-all.ru/3.11/library/functools.html).2021Возвращает то же, что и `lru_cache(maxsize=None)`, создавая тонкую обёртку вокруг поиска по словарю для аргументов функции. Поскольку ему никогда не нужно вытеснять старые значения, он меньше и быстрее, чем [`lru_cache()`](https://python-all.ru/3.11/library/functools.html#functools.lru_cache) с ограничением размера.2223Например:2425```python26@cache27def factorial(n):28 return n * factorial(n-1) if n else 12930>>> factorial(10) # результат ранее не кэшировался, выполняется 11 рекурсивных вызовов31362880032>>> factorial(5) # просто возвращает закешированное значение результата3312034>>> factorial(12) # делает два новых рекурсивных вызова, остальные 10 закешированы3547900160036```3738Кэш потокобезопасен, поэтому обёрнутую функцию можно использовать в нескольких потоках. Это означает, что базовая структура данных будет оставаться согласованной при конкурентных обновлениях.3940Обёрнутая функция может быть вызвана более одного раза, если другой поток делает дополнительный вызов до того, как первый вызов будет завершён и закэширован.4142Новое в версии 3.9.4344#### `@functools.cached_property(func)`4546Преобразует метод класса в свойство, значение которого вычисляется один раз и затем кэшируется как обычный атрибут на всё время существования экземпляра. Аналогично [`property()`](https://python-all.ru/3.11/library/functions.html#property), но с добавлением кэширования. Полезно для дорогостоящих вычисляемых свойств экземпляров, которые в остальном практически неизменны.4748Пример:4950```python51class DataSet:5253 def __init__(self, sequence_of_numbers):54 self._data = tuple(sequence_of_numbers)5556 @cached_property57 def stdev(self):58 return statistics.stdev(self._data)59```6061Механика [`cached_property()`](https://python-all.ru/3.11/library/functools.html#functools.cached_property) несколько отличается от [`property()`](https://python-all.ru/3.11/library/functions.html#property). Обычное свойство блокирует запись атрибута, если не определён сеттер. Напротив, *cached\_property* разрешает запись.6263Декоратор *cached\_property* выполняется только при поиске атрибута и только когда атрибут с таким именем не существует. Когда он выполняется, *cached\_property* записывает значение в атрибут с тем же именем. Последующие чтения и записи атрибута имеют приоритет над методом *cached\_property*, и он работает как обычный атрибут.6465Кэшированное значение можно очистить, удалив атрибут. Это позволяет методу *cached\_property* выполниться снова.6667Примечание: этот декоратор мешает работе [**PEP 412**](https://python-all.ru/3.11/library/functools.html) словарей с разделением ключей. Это означает, что словари экземпляров могут занимать больше места, чем обычно.6869Кроме того, этот декоратор требует, чтобы атрибут `__dict__` на каждом экземпляре был изменяемым отображением. Это означает, что он не будет работать с некоторыми типами, такими как метаклассы (поскольку атрибуты `__dict__` на экземплярах типа являются прокси только для чтения пространства имён класса), и теми, которые указывают `__slots__` без включения `__dict__` в качестве одного из определённых слотов (так как такие классы вообще не предоставляют атрибут `__dict__`).7071Если изменяемое отображение недоступно или требуется эффективное по памяти разделение ключей, эффекта, аналогичного [`cached_property()`](https://python-all.ru/3.11/library/functools.html#functools.cached_property), можно также достичь, разместив [`property()`](https://python-all.ru/3.11/library/functions.html#property) поверх [`lru_cache()`](https://python-all.ru/3.11/library/functools.html#functools.lru_cache). Смотрите [Как кэшировать вызовы методов?](https://python-all.ru/3.11/faq/programming.html#faq-cache-method-calls) для получения дополнительных сведений о том, чем это отличается от [`cached_property()`](https://python-all.ru/3.11/library/functools.html#functools.cached_property).7273Новое в версии 3.8.7475#### `functools.cmp_to_key(func)`7677Преобразует старую функцию сравнения в [функцию ключа](https://python-all.ru/3.11/glossary.html#term-key-function). Используется с инструментами, которые принимают функции ключа (такими как [`sorted()`](https://python-all.ru/3.11/library/functions.html#sorted), [`min()`](https://python-all.ru/3.11/library/functions.html#min), [`max()`](https://python-all.ru/3.11/library/functions.html#max), [`heapq.nlargest()`](https://python-all.ru/3.11/library/heapq.html#heapq.nlargest), [`heapq.nsmallest()`](https://python-all.ru/3.11/library/heapq.html#heapq.nsmallest), [`itertools.groupby()`](https://python-all.ru/3.11/library/itertools.html#itertools.groupby)). Эта функция в первую очередь используется как инструмент перехода для программ, переносимых с Python 2, который поддерживал использование функций сравнения.7879Функция сравнения – это любой вызываемый объект, который принимает два аргумента, сравнивает их и возвращает отрицательное число для «меньше», ноль для «равно» или положительное число для «больше». Функция ключа – это вызываемый объект, который принимает один аргумент и возвращает другое значение, используемое как ключ сортировки.8081Пример:8283```python84sorted(iterable, key=cmp_to_key(locale.strcoll)) # порядок сортировки с учётом локали85```8687Примеры сортировки и краткое руководство по ней см. в [Sorting HOW TO](https://python-all.ru/3.11/howto/sorting.html#sortinghowto).8889Новое в версии 3.2.9091#### `@functools.lru_cache(user_function)`9293#### `@functools.lru_cache(maxsize=128, typed=False)`9495Декоратор для обёртывания функции запоминающим вызываемым объектом, который сохраняет до *maxsize* последних вызовов. Это может сэкономить время, когда дорогостоящая или связанная с вводом-выводом функция периодически вызывается с одними и теми же аргументами.9697Кэш потокобезопасен, поэтому обёрнутую функцию можно использовать в нескольких потоках. Это означает, что базовая структура данных будет оставаться согласованной при конкурентных обновлениях.9899Обёрнутая функция может быть вызвана более одного раза, если другой поток делает дополнительный вызов до того, как первый вызов будет завершён и закэширован.100101Поскольку для кэширования результатов используется словарь, позиционные и именованные аргументы функции должны быть [хэшируемыми](https://python-all.ru/3.11/glossary.html#term-hashable).102103Разные шаблоны аргументов могут считаться отдельными вызовами с отдельными записями в кэше. Например, `f(a=1, b=2)` и `f(b=2, a=1)` отличаются порядком именованных аргументов и могут иметь две отдельные записи в кэше.104105Если указан *user\_function*, он должен быть вызываемым. Это позволяет применить декоратор *lru\_cache* непосредственно к пользовательской функции, оставляя *maxsize* со значением по умолчанию 128:106107```python108@lru_cache109def count_vowels(sentence):110 return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')111```112113Если *maxsize* установлен в `None`, механизм LRU отключается и кэш может расти без ограничений.114115Если *typed* установлен в true, аргументы функций разных типов будут кэшироваться отдельно. Если *typed* равен false, реализация обычно считает их эквивалентными вызовами и кэширует только один результат. (Некоторые типы, такие как *str* и *int*, могут кэшироваться отдельно, даже если *typed* равен false.)116117Обратите внимание: специфичность типа применяется только к непосредственным аргументам функции, а не к их содержимому. Скалярные аргументы `Decimal(42)` и `Fraction(42)` обрабатываются как разные вызовы с разными результатами. В отличие от них, аргументы-кортежи `('answer', Decimal(42))` и `('answer', Fraction(42))` считаются эквивалентными.118119Обёрнутая функция оснащается `cache_parameters()` функцией, которая возвращает новый [`dict`](https://python-all.ru/3.11/library/stdtypes.html#dict), отображающий значения *maxsize* и *typed*. Это только для информационных целей. Изменение значений не влияет на поведение.120121Чтобы оценить эффективность кэша и настроить параметр *maxsize*, обёрнутая функция оснащается `cache_info()` функцией, которая возвращает [именованный кортеж](https://python-all.ru/3.11/glossary.html#term-named-tuple), показывающий *hits*, *misses*, *maxsize* и *currsize*.122123Декоратор также предоставляет функцию `cache_clear()` для очистки или инвалидации кэша.124125Исходная базовая функция доступна через атрибут `__wrapped__`. Это полезно для интроспекции, обхода кэша или повторной обёртки функции другим кэшем.126127Кэш хранит ссылки на аргументы и возвращаемые значения до тех пор, пока они не устареют и не будут вытеснены из кэша, или пока кэш не будет очищен.128129Если кэшируется метод, аргумент экземпляра `self` также попадает в кэш. См. [Как кэшировать вызовы методов?](https://python-all.ru/3.11/faq/programming.html#faq-cache-method-calls)130131[LRU-кэш (вытесняющий давно неиспользовавшиеся элементы)](https://python-all.ru/3.11/library/functools.html) работает лучше всего, когда самые недавние вызовы являются наилучшими предикторами будущих вызовов (например, самые популярные статьи на новостном сервере обычно меняются каждый день). Ограничение размера кэша гарантирует, что кэш не будет расти бесконечно в долго работающих процессах, таких как веб-серверы.132133В целом, LRU-кэш следует использовать только тогда, когда нужно повторно использовать ранее вычисленные значения. Соответственно, не имеет смысла кэшировать функции с побочными эффектами, функции, которые должны создавать различные изменяемые объекты при каждом вызове (например, генераторы и асинхронные функции), или нечистые функции, такие как time() или random().134135Пример LRU-кэша для статического веб-контента:136137```python138@lru_cache(maxsize=32)139def get_pep(num):140 'Retrieve text of a Python Enhancement Proposal'141 resource = 'https://peps.python.org/pep-%04d/' % num142 try:143 with urllib.request.urlopen(resource) as s:144 return s.read()145 except urllib.error.HTTPError:146 return 'Not Found'147148>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:149... pep = get_pep(n)150... print(n, len(pep))151152>>> get_pep.cache_info()153CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)154```155156Пример эффективного вычисления [чисел Фибоначчи](https://python-all.ru/3.11/library/functools.html) с использованием кэша для реализации техники [динамического программирования](https://python-all.ru/3.11/library/functools.html):157158```python159@lru_cache(maxsize=None)160def fib(n):161 if n < 2:162 return n163 return fib(n-1) + fib(n-2)164165>>> [fib(n) for n in range(16)]166[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]167168>>> fib.cache_info()169CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)170```171172Новое в версии 3.2.173174Изменено в версии 3.3: Добавлена опция *typed*.175176Изменено в версии 3.8: Добавлена опция *user\_function*.177178Изменено в версии 3.9: Добавлена функция `cache_parameters()`179180#### `@functools.total_ordering`181182Для класса, определяющего один или несколько методов расширенного сравнения, этот декоратор класса предоставляет остальные. Это упрощает задачу определения всех возможных операций расширенного сравнения:183184Класс должен определять один из `__lt__()`, `__le__()`, `__gt__()` или `__ge__()`. Кроме того, класс должен предоставлять метод `__eq__()`.185186Например:187188```python189@total_ordering190class Student:191 def _is_valid_operand(self, other):192 return (hasattr(other, "lastname") and193 hasattr(other, "firstname"))194 def __eq__(self, other):195 if not self._is_valid_operand(other):196 return NotImplemented197 return ((self.lastname.lower(), self.firstname.lower()) ==198 (other.lastname.lower(), other.firstname.lower()))199 def __lt__(self, other):200 if not self._is_valid_operand(other):201 return NotImplemented202 return ((self.lastname.lower(), self.firstname.lower()) <203 (other.lastname.lower(), other.firstname.lower()))204```205206> **Примечание**207>208> Хотя этот декоратор упрощает создание хорошо ведущих себя полностью упорядоченных типов, он *действительно* приводит к более медленному выполнению и более сложным трассировкам стека для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для конкретного приложения, реализация всех шести методов расширенного сравнения вместо этого, вероятно, даст лёгкий прирост скорости.209210> **Примечание**211>212> Этот декоратор не пытается переопределить методы, объявленные в классе *или его суперклассах*. Это означает, что если суперкласс определяет оператор сравнения, *total\_ordering* не будет реализовывать его снова, даже если исходный метод является абстрактным.213214Новое в версии 3.2.215216Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из нижележащей функции сравнения для нераспознанных типов.217218#### `functools.partial(func, /, *args, **keywords)`219220Возвращает новый [объект partial](https://python-all.ru/3.11/library/functools.html#partial-objects), который при вызове будет вести себя как *func*, вызванная с позиционными аргументами *args* и именованными аргументами *keywords*. Если при вызове передаётся больше аргументов, они добавляются к *args*. Если передаются дополнительные именованные аргументы, они расширяют и переопределяют *keywords*. Примерно эквивалентно следующему:221222```python223def partial(func, /, *args, **keywords):224 def newfunc(*fargs, **fkeywords):225 newkeywords = {**keywords, **fkeywords}226 return func(*args, *fargs, **newkeywords)227 newfunc.func = func228 newfunc.args = args229 newfunc.keywords = keywords230 return newfunc231```232233[`partial()`](https://python-all.ru/3.11/library/functools.html#functools.partial) используется для частичного применения функции, которое «замораживает» некоторую часть аргументов и/или ключевых слов функции, создавая новый объект с упрощённой сигнатурой. Например, [`partial()`](https://python-all.ru/3.11/library/functools.html#functools.partial) можно использовать для создания вызываемого объекта, который ведёт себя как функция [`int()`](https://python-all.ru/3.11/library/functions.html#int), где аргумент *base* по умолчанию равен двум:234235```python236>>> from functools import partial237>>> basetwo = partial(int, base=2)238>>> basetwo.__doc__ = 'Convert base 2 string to an int.'239>>> basetwo('10010')24018241```242243#### `class functools.partialmethod(func, /, *args, **keywords)`244245Возвращает новый дескриптор [`partialmethod`](https://python-all.ru/3.11/library/functools.html#functools.partialmethod), который ведёт себя как [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial), за исключением того, что он предназначен для использования в качестве определения метода, а не для прямого вызова.246247*func* должен быть [дескриптором](https://python-all.ru/3.11/glossary.html#term-descriptor) или вызываемым объектом (объекты, являющиеся и тем и другим, как обычные функции, обрабатываются как дескрипторы).248249Когда *func* является дескриптором (например, обычная функция Python, [`classmethod()`](https://python-all.ru/3.11/library/functions.html#classmethod), [`staticmethod()`](https://python-all.ru/3.11/library/functions.html#staticmethod), `abstractmethod()` или другой экземпляр [`partialmethod`](https://python-all.ru/3.11/library/functools.html#functools.partialmethod)), вызовы `__get__` делегируются базовому дескриптору, а в качестве результата возвращается подходящий [частичный объект](https://python-all.ru/3.11/library/functools.html#partial-objects).250251Когда *func* – это вызываемый объект, не являющийся дескриптором, динамически создаётся соответствующий связанный метод. Он ведёт себя как обычная функция Python при использовании в качестве метода: аргумент *self* будет вставлен как первый позиционный аргумент, даже до *args* и *keywords*, переданных конструктору [`partialmethod`](https://python-all.ru/3.11/library/functools.html#functools.partialmethod).252253Пример:254255```python256>>> class Cell:257... def __init__(self):258... self._alive = False259... @property260... def alive(self):261... return self._alive262... def set_state(self, state):263... self._alive = bool(state)264... set_alive = partialmethod(set_state, True)265... set_dead = partialmethod(set_state, False)266...267>>> c = Cell()268>>> c.alive269False270>>> c.set_alive()271>>> c.alive272True273```274275Новое в версии 3.4.276277#### `functools.reduce(function, iterable[, initializer])`278279Применяет *функцию* с двумя аргументами последовательно к элементам *итерируемого объекта* слева направо, чтобы свести итерируемый объект к одному значению. Например, `reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` вычисляет `((((1+2)+3)+4)+5)`. Левый аргумент *x* – накопленное значение, а правый аргумент *y* – очередное значение из *итерируемого объекта*. Если указан необязательный *инициализатор*, он помещается перед элементами итерируемого объекта в вычислениях и служит значением по умолчанию, когда итерируемый объект пуст. Если *инициализатор* не указан, а *итерируемый объект* содержит только один элемент, возвращается первый элемент.280281Примерно эквивалентно:282283```python284def reduce(function, iterable, initializer=None):285 it = iter(iterable)286 if initializer is None:287 value = next(it)288 else:289 value = initializer290 for element in it:291 value = function(value, element)292 return value293```294295См. [`itertools.accumulate()`](https://python-all.ru/3.11/library/itertools.html#itertools.accumulate) для итератора, который возвращает все промежуточные значения.296297#### `@functools.singledispatch`298299Преобразует функцию в [однодиспетчерскую](https://python-all.ru/3.11/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.11/glossary.html#term-generic-function).300301Чтобы определить обобщённую функцию, декорируйте её с помощью декоратора `@singledispatch`. При определении функции с помощью `@singledispatch` учтите, что диспетчеризация происходит по типу первого аргумента:302303```python304>>> from functools import singledispatch305>>> @singledispatch306... def fun(arg, verbose=False):307... if verbose:308... print("Let me just say,", end=" ")309... print(arg)310```311312Чтобы добавить перегруженные реализации функции, используйте атрибут `register()` обобщённой функции, который можно использовать как декоратор. Для функций с аннотациями типов декоратор автоматически определяет тип первого аргумента:313314```python315>>> @fun.register316... def _(arg: int, verbose=False):317... if verbose:318... print("Strength in numbers, eh?", end=" ")319... print(arg)320...321>>> @fun.register322... def _(arg: list, verbose=False):323... if verbose:324... print("Enumerate this:")325... for i, elem in enumerate(arg):326... print(i, elem)327```328329[`types.UnionType`](https://python-all.ru/3.11/library/types.html#types.UnionType) и [`typing.Union`](https://python-all.ru/3.11/library/typing.html#typing.Union) также могут использоваться:330331```python332>>> @fun.register333... def _(arg: int | float, verbose=False):334... if verbose:335... print("Strength in numbers, eh?", end=" ")336... print(arg)337...338>>> from typing import Union339>>> @fun.register340... def _(arg: Union[list, set], verbose=False):341... if verbose:342... print("Enumerate this:")343... for i, elem in enumerate(arg):344... print(i, elem)345...346```347348Для кода, не использующего аннотации типов, подходящий тип можно передать явно самому декоратору:349350```python351>>> @fun.register(complex)352... def _(arg, verbose=False):353... if verbose:354... print("Better than complicated.", end=" ")355... print(arg.real, arg.imag)356...357```358359Чтобы можно было регистрировать [лямбды](https://python-all.ru/3.11/glossary.html#term-lambda) и уже существующие функции, атрибут `register()` также можно использовать в функциональной форме:360361```python362>>> def nothing(arg, verbose=False):363... print("Nothing.")364...365>>> fun.register(type(None), nothing)366```367368Атрибут `register()` возвращает недекорированную функцию. Это позволяет накладывать декораторы, [`pickling`](https://python-all.ru/3.11/library/pickle.html#module-pickle) и создавать модульные тесты для каждого варианта независимо:369370```python371>>> @fun.register(float)372... @fun.register(Decimal)373... def fun_num(arg, verbose=False):374... if verbose:375... print("Half of your number:", end=" ")376... print(arg / 2)377...378>>> fun_num is fun379False380```381382При вызове обобщённая функция выполняет диспетчеризацию по типу первого аргумента:383384```python385>>> fun("Hello, world.")386Hello, world.387>>> fun("test.", verbose=True)388Let me just say, test.389>>> fun(42, verbose=True)390Strength in numbers, eh? 42391>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)392Enumerate this:3930 spam3941 spam3952 eggs3963 spam397>>> fun(None)398Nothing.399>>> fun(1.23)4000.615401```402403Если для конкретного типа нет зарегистрированной реализации, используется порядок разрешения методов (MRO) этого типа для поиска более общей реализации. Исходная функция, декорированная `@singledispatch`, зарегистрирована для базового типа [`object`](https://python-all.ru/3.11/library/functions.html#object), то есть она используется, если не найдено более подходящей реализации.404405Если реализация зарегистрирована для [абстрактного базового класса](https://python-all.ru/3.11/glossary.html#term-abstract-base-class), виртуальные подклассы этого базового класса будут диспетчеризоваться к этой реализации:406407```python408>>> from collections.abc import Mapping409>>> @fun.register410... def _(arg: Mapping, verbose=False):411... if verbose:412... print("Keys & Values")413... for key, value in arg.items():414... print(key, "=>", value)415...416>>> fun({"a": "b"})417a => b418```419420Чтобы проверить, какую реализацию выберет обобщённая функция для заданного типа, используйте атрибут `dispatch()`:421422```python423>>> fun.dispatch(float)424<function fun_num at 0x1035a2840>425>>> fun.dispatch(dict) # примечание: реализация по умолчанию426<function fun at 0x103fe0000>427```428429Чтобы получить доступ ко всем зарегистрированным реализациям, используйте атрибут `registry` только для чтения:430431```python432>>> fun.registry.keys()433dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,434 <class 'decimal.Decimal'>, <class 'list'>,435 <class 'float'>])436>>> fun.registry[float]437<function fun_num at 0x1035a2840>438>>> fun.registry[object]439<function fun at 0x103fe0000>440```441442Новое в версии 3.4.443444Изменено в версии 3.7: Атрибут `register()` теперь поддерживает использование аннотаций типов.445446Изменено в версии 3.11: Атрибут `register()` теперь поддерживает [`types.UnionType`](https://python-all.ru/3.11/library/types.html#types.UnionType) и [`typing.Union`](https://python-all.ru/3.11/library/typing.html#typing.Union) в качестве аннотаций типов.447448#### `class functools.singledispatchmethod(func)`449450Преобразует метод в [однодиспетчерскую](https://python-all.ru/3.11/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.11/glossary.html#term-generic-function).451452Чтобы определить обобщённый метод, декорируйте его декоратором `@singledispatchmethod`. При определении функции с помощью `@singledispatchmethod` учтите, что диспетчеризация выполняется по типу первого аргумента, отличного от *self* или *cls*:453454```python455class Negator:456 @singledispatchmethod457 def neg(self, arg):458 raise NotImplementedError("Cannot negate a")459460 @neg.register461 def _(self, arg: int):462 return -arg463464 @neg.register465 def _(self, arg: bool):466 return not arg467```468469`@singledispatchmethod` поддерживает вложение с другими декораторами, такими как [`@classmethod`](https://python-all.ru/3.11/library/functions.html#classmethod). Обратите внимание, что для поддержки `dispatcher.register` `singledispatchmethod` должен быть *самым внешним* декоратором. Вот класс `Negator` с методами `neg`, привязанными к классу, а не к экземпляру класса:470471```python472class Negator:473 @singledispatchmethod474 @classmethod475 def neg(cls, arg):476 raise NotImplementedError("Cannot negate a")477478 @neg.register479 @classmethod480 def _(cls, arg: int):481 return -arg482483 @neg.register484 @classmethod485 def _(cls, arg: bool):486 return not arg487```488489Этот же подход можно использовать для других подобных декораторов: [`@staticmethod`](https://python-all.ru/3.11/library/functions.html#staticmethod), [`@abstractmethod`](https://python-all.ru/3.11/library/abc.html#abc.abstractmethod) и других.490491Новое в версии 3.8.492493#### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`494495Обновляет функцию-*обёртку* так, чтобы она походила на *обёрнутую* функцию. Необязательные аргументы – это кортежи, определяющие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки, а какие атрибуты функции-обёртки обновляются соответствующими атрибутами из исходной функции. Значения по умолчанию для этих аргументов – константы уровня модуля `WRAPPER_ASSIGNMENTS` (которая присваивает функции-обёртке атрибуты `__module__`, `__name__`, `__qualname__`, `__annotations__` и `__doc__`, строку документации) и `WRAPPER_UPDATES` (которая обновляет `__dict__` функции-обёртки, т.е. словарь экземпляра).496497Чтобы обеспечить доступ к исходной функции для интроспекции и других целей (например, обхода кэширующего декоратора такого как [`lru_cache()`](https://python-all.ru/3.11/library/functools.html#functools.lru_cache)), эта функция автоматически добавляет атрибут `__wrapped__` к обёртке, который ссылается на обёрнутую функцию.498499Основное назначение этой функции – использование в [decorator](https://python-all.ru/3.11/glossary.html#term-decorator)-функциях, которые оборачивают декорированную функцию и возвращают обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно не очень полезно.500501[`update_wrapper()`](https://python-all.ru/3.11/library/functools.html#functools.update_wrapper) можно использовать с вызываемыми объектами, отличными от функций. Любые атрибуты, указанные в *assigned* или *updated*, отсутствующие в оборачиваемом объекте, игнорируются (т.е. эта функция не будет пытаться установить их в функции-обёртке). [`AttributeError`](https://python-all.ru/3.11/library/exceptions.html#AttributeError) по-прежнему возбуждается, если сама функция-обёртка не имеет каких-либо атрибутов, перечисленных в *updated*.502503Изменено в версии 3.2: Атрибут `__wrapped__` теперь добавляется автоматически. Атрибут `__annotations__` теперь копируется по умолчанию. Отсутствующие атрибуты больше не вызывают [`AttributeError`](https://python-all.ru/3.11/library/exceptions.html#AttributeError).504505Изменено в версии 3.4: Атрибут `__wrapped__` теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут `__wrapped__`. (см. [bpo-17482](https://python-all.ru/3.11/library/functools.html))506507#### `@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`508509Это удобная функция для вызова [`update_wrapper()`](https://python-all.ru/3.11/library/functools.html#functools.update_wrapper) в качестве декоратора функции при определении функции-обёртки. Она эквивалентна `partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`. Например:510511```python512>>> from functools import wraps513>>> def my_decorator(f):514... @wraps(f)515... def wrapper(*args, **kwds):516... print('Calling decorated function')517... return f(*args, **kwds)518... return wrapper519...520>>> @my_decorator521... def example():522... """докстринг"""523... print('Called example function')524...525>>> example()526Calling decorated function527Called example function528>>> example.__name__529'example'530>>> example.__doc__531'Docstring'532```533534Без использования этой фабрики декораторов имя примера функции было бы `'wrapper'`, а строка документации исходной `example()` была бы потеряна.535536## [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial) Объекты537538Объекты [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial) – это вызываемые объекты, создаваемые [`partial()`](https://python-all.ru/3.11/library/functools.html#functools.partial). Они имеют три атрибута только для чтения:539540#### `partial.func`541542Вызываемый объект или функция. Вызовы объекта [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial) будут перенаправлены в [`func`](https://python-all.ru/3.11/library/functools.html#functools.partial.func) с новыми аргументами и ключевыми словами.543544#### `partial.args`545546Крайние левые позиционные аргументы, которые будут добавлены перед позиционными аргументами, переданными при вызове объекта [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial).547548#### `partial.keywords`549550Ключевые аргументы, которые будут предоставлены при вызове объекта [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial).551552Объекты [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial) похожи на объекты `function` тем, что они вызываемы, на них можно ссылаться слабо и они могут иметь атрибуты. Есть несколько важных отличий. Например, атрибуты [`__name__`](https://python-all.ru/3.11/library/stdtypes.html#definition.__name__) и `__doc__` не создаются автоматически. Кроме того, объекты [`partial`](https://python-all.ru/3.11/library/functools.html#functools.partial), определённые в классах, ведут себя как статические методы и не превращаются в связанные методы при поиске атрибута экземпляра.553