functools.md
1> **Источник:** https://python-all.ru/3.10/library/functools.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`functools`](https://python-all.ru/3.10/library/functools.html#module-functools) – Функции высшего порядка и операции над вызываемыми объектами89**Исходный код:** [Lib/functools.py](https://python-all.ru/src/3.10/Lib/functools.py)1011---1213Модуль [`functools`](https://python-all.ru/3.10/library/functools.html#module-functools) предназначен для функций высшего порядка: функций, которые действуют на или возвращают другие функции. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.1415Модуль [`functools`](https://python-all.ru/3.10/library/functools.html#module-functools) определяет следующие функции:1617#### `@functools.cache(user_function)`1819Простой легковесный кэш функций без ограничения размера. Иногда называется [«мемоизация»](https://python-all.ru/3.10/library/functools.html).2021Возвращает то же, что и `lru_cache(maxsize=None)`, создавая тонкую обёртку вокруг поиска по словарю для аргументов функции. Поскольку ему никогда не нужно вытеснять старые значения, он меньше и быстрее, чем [`lru_cache()`](https://python-all.ru/3.10/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Новое в версии 3.9.3940#### `@functools.cached_property(func)`4142Преобразует метод класса в свойство, значение которого вычисляется один раз и затем кэшируется как обычный атрибут на всё время существования экземпляра. Аналогично [`property()`](https://python-all.ru/3.10/library/functions.html#property), но с добавлением кэширования. Полезно для дорогостоящих вычисляемых свойств экземпляров, которые в остальном практически неизменны.4344Пример:4546```python47class DataSet:4849 def __init__(self, sequence_of_numbers):50 self._data = tuple(sequence_of_numbers)5152 @cached_property53 def stdev(self):54 return statistics.stdev(self._data)55```5657Механика [`cached_property()`](https://python-all.ru/3.10/library/functools.html#functools.cached_property) несколько отличается от [`property()`](https://python-all.ru/3.10/library/functions.html#property). Обычное свойство блокирует запись атрибута, если не определён сеттер. Напротив, *cached\_property* разрешает запись.5859Декоратор *cached\_property* выполняется только при поиске атрибута и только когда атрибут с таким именем не существует. Когда он выполняется, *cached\_property* записывает значение в атрибут с тем же именем. Последующие чтения и записи атрибута имеют приоритет над методом *cached\_property*, и он работает как обычный атрибут.6061Кэшированное значение можно очистить, удалив атрибут. Это позволяет методу *cached\_property* выполниться снова.6263Примечание: этот декоратор мешает работе [**PEP 412**](https://python-all.ru/3.10/library/functools.html) словарей с разделением ключей. Это означает, что словари экземпляров могут занимать больше места, чем обычно.6465Кроме того, этот декоратор требует, чтобы атрибут `__dict__` на каждом экземпляре был изменяемым отображением. Это означает, что он не будет работать с некоторыми типами, такими как метаклассы (поскольку атрибуты `__dict__` на экземплярах типа являются прокси только для чтения пространства имён класса), и теми, которые указывают `__slots__` без включения `__dict__` в качестве одного из определённых слотов (так как такие классы вообще не предоставляют атрибут `__dict__`).6667Если изменяемое отображение недоступно или требуется экономное по памяти совместное использование ключей, эффект, подобный [`cached_property()`](https://python-all.ru/3.10/library/functools.html#functools.cached_property), можно получить, разместив [`property()`](https://python-all.ru/3.10/library/functions.html#property) поверх [`cache()`](https://python-all.ru/3.10/library/functools.html#functools.cache):6869```python70class DataSet:71 def __init__(self, sequence_of_numbers):72 self._data = sequence_of_numbers7374 @property75 @cache76 def stdev(self):77 return statistics.stdev(self._data)78```7980Новое в версии 3.8.8182#### `functools.cmp_to_key(func)`8384Преобразует старую функцию сравнения в [функцию ключа](https://python-all.ru/3.10/glossary.html#term-key-function). Используется с инструментами, которые принимают функции ключа (такими как [`sorted()`](https://python-all.ru/3.10/library/functions.html#sorted), [`min()`](https://python-all.ru/3.10/library/functions.html#min), [`max()`](https://python-all.ru/3.10/library/functions.html#max), [`heapq.nlargest()`](https://python-all.ru/3.10/library/heapq.html#heapq.nlargest), [`heapq.nsmallest()`](https://python-all.ru/3.10/library/heapq.html#heapq.nsmallest), [`itertools.groupby()`](https://python-all.ru/3.10/library/itertools.html#itertools.groupby)). Эта функция в первую очередь используется как инструмент перехода для программ, переносимых с Python 2, который поддерживал использование функций сравнения.8586Функция сравнения – это любой вызываемый объект, который принимает два аргумента, сравнивает их и возвращает отрицательное число для «меньше», ноль для «равно» или положительное число для «больше». Функция ключа – это вызываемый объект, который принимает один аргумент и возвращает другое значение, используемое как ключ сортировки.8788Пример:8990```python91sorted(iterable, key=cmp_to_key(locale.strcoll)) # порядок сортировки с учётом локали92```9394Примеры сортировки и краткое руководство см. в [Sorting HOW TO](https://python-all.ru/3.10/howto/sorting.html#sortinghowto).9596Новое в версии 3.2.9798#### `@functools.lru_cache(user_function)`99100#### `@functools.lru_cache(maxsize=128, typed=False)`101102Декоратор для обёртывания функции запоминающим вызываемым объектом, который сохраняет до *maxsize* последних вызовов. Это может сэкономить время, когда дорогостоящая или связанная с вводом-выводом функция периодически вызывается с одними и теми же аргументами.103104Поскольку для кэширования результатов используется словарь, позиционные и именованные аргументы функции должны быть [хэшируемыми](https://python-all.ru/3.10/glossary.html#term-hashable).105106Различные наборы аргументов могут считаться разными вызовами с отдельными записями в кеше. Например, *f(a=1, b=2)* и *f(b=2, a=1)* различаются порядком именованных аргументов и могут иметь две отдельные записи в кеше.107108Если указан *user\_function*, он должен быть вызываемым. Это позволяет применить декоратор *lru\_cache* непосредственно к пользовательской функции, оставляя *maxsize* со значением по умолчанию 128:109110```python111@lru_cache112def count_vowels(sentence):113 return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')114```115116Если *maxsize* установлен в `None`, механизм LRU отключается и кэш может расти без ограничений.117118Если *typed* установлен в true, аргументы функций разных типов будут кэшироваться отдельно. Если *typed* равен false, реализация обычно считает их эквивалентными вызовами и кэширует только один результат. (Некоторые типы, такие как *str* и *int*, могут кэшироваться отдельно, даже если *typed* равен false.)119120Обратите внимание: специфичность типа применяется только к непосредственным аргументам функции, а не к их содержимому. Скалярные аргументы `Decimal(42)` и `Fraction(42)` обрабатываются как разные вызовы с разными результатами. В отличие от них, аргументы-кортежи `('answer', Decimal(42))` и `('answer', Fraction(42))` считаются эквивалентными.121122Обёрнутая функция оснащается `cache_parameters()` функцией, которая возвращает новый [`dict`](https://python-all.ru/3.10/library/stdtypes.html#dict), отображающий значения *maxsize* и *typed*. Это только для информационных целей. Изменение значений не влияет на поведение.123124Чтобы оценить эффективность кэша и настроить параметр *maxsize*, обёрнутая функция оснащается `cache_info()` функцией, которая возвращает [именованный кортеж](https://python-all.ru/3.10/glossary.html#term-named-tuple), показывающий *hits*, *misses*, *maxsize* и *currsize*.125126Декоратор также предоставляет функцию `cache_clear()` для очистки или инвалидации кэша.127128Исходная базовая функция доступна через атрибут `__wrapped__`. Это полезно для интроспекции, обхода кэша или повторной обёртки функции другим кэшем.129130Кэш хранит ссылки на аргументы и возвращаемые значения до тех пор, пока они не устареют и не будут вытеснены из кэша, или пока кэш не будет очищен.131132[LRU-кэш (вытесняющий давно неиспользовавшиеся элементы)](https://python-all.ru/3.10/library/functools.html) работает лучше всего, когда самые недавние вызовы являются наилучшими предикторами будущих вызовов (например, самые популярные статьи на новостном сервере обычно меняются каждый день). Ограничение размера кэша гарантирует, что кэш не будет расти бесконечно в долго работающих процессах, таких как веб-серверы.133134В целом, LRU-кеш следует использовать только тогда, когда нужно повторно использовать ранее вычисленные значения. Соответственно, нет смысла кешировать функции с побочными эффектами, функции, которым нужно создавать различные изменяемые объекты при каждом вызове, или нечистые функции, такие как time() или random().135136Пример LRU-кэша для статического веб-контента:137138```python139@lru_cache(maxsize=32)140def get_pep(num):141 'Retrieve text of a Python Enhancement Proposal'142 resource = 'https://www.python.org/dev/peps/pep-%04d/' % num143 try:144 with urllib.request.urlopen(resource) as s:145 return s.read()146 except urllib.error.HTTPError:147 return 'Not Found'148149>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:150... pep = get_pep(n)151... print(n, len(pep))152153>>> get_pep.cache_info()154CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)155```156157Пример эффективного вычисления [чисел Фибоначчи](https://python-all.ru/3.10/library/functools.html) с использованием кэша для реализации техники [динамического программирования](https://python-all.ru/3.10/library/functools.html):158159```python160@lru_cache(maxsize=None)161def fib(n):162 if n < 2:163 return n164 return fib(n-1) + fib(n-2)165166>>> [fib(n) for n in range(16)]167[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]168169>>> fib.cache_info()170CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)171```172173Новое в версии 3.2.174175Изменено в версии 3.3: Добавлена опция *typed*.176177Изменено в версии 3.8: Добавлена опция *user\_function*.178179Новое в версии 3.9: Добавлена функция `cache_parameters()`180181#### `@functools.total_ordering`182183Для класса, определяющего один или несколько методов расширенного сравнения, этот декоратор класса предоставляет остальные. Это упрощает задачу определения всех возможных операций расширенного сравнения:184185Класс должен определять один из `__lt__()`, `__le__()`, `__gt__()` или `__ge__()`. Кроме того, класс должен предоставлять метод `__eq__()`.186187Например:188189```python190@total_ordering191class Student:192 def _is_valid_operand(self, other):193 return (hasattr(other, "lastname") and194 hasattr(other, "firstname"))195 def __eq__(self, other):196 if not self._is_valid_operand(other):197 return NotImplemented198 return ((self.lastname.lower(), self.firstname.lower()) ==199 (other.lastname.lower(), other.firstname.lower()))200 def __lt__(self, other):201 if not self._is_valid_operand(other):202 return NotImplemented203 return ((self.lastname.lower(), self.firstname.lower()) <204 (other.lastname.lower(), other.firstname.lower()))205```206207> **Примечание**208>209> Хотя этот декоратор упрощает создание хорошо ведущих себя полностью упорядоченных типов, он *действительно* приводит к более медленному выполнению и более сложным трассировкам стека для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для конкретного приложения, реализация всех шести методов расширенного сравнения вместо этого, вероятно, даст лёгкий прирост скорости.210211> **Примечание**212>213> Этот декоратор не пытается переопределить методы, объявленные в классе *или его суперклассах*. Это означает, что если суперкласс определяет оператор сравнения, *total\_ordering* не будет реализовывать его снова, даже если исходный метод является абстрактным.214215Новое в версии 3.2.216217Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из нижележащей функции сравнения для нераспознанных типов.218219#### `functools.partial(func, /, *args, **keywords)`220221Возвращает новый [объект partial](https://python-all.ru/3.10/library/functools.html#partial-objects), который при вызове будет вести себя как *func*, вызванная с позиционными аргументами *args* и именованными аргументами *keywords*. Если при вызове передаётся больше аргументов, они добавляются к *args*. Если передаются дополнительные именованные аргументы, они расширяют и переопределяют *keywords*. Примерно эквивалентно следующему:222223```python224def partial(func, /, *args, **keywords):225 def newfunc(*fargs, **fkeywords):226 newkeywords = {**keywords, **fkeywords}227 return func(*args, *fargs, **newkeywords)228 newfunc.func = func229 newfunc.args = args230 newfunc.keywords = keywords231 return newfunc232```233234[`partial()`](https://python-all.ru/3.10/library/functools.html#functools.partial) используется для частичного применения функции, которое «замораживает» некоторую часть аргументов и/или ключевых слов функции, создавая новый объект с упрощённой сигнатурой. Например, [`partial()`](https://python-all.ru/3.10/library/functools.html#functools.partial) можно использовать для создания вызываемого объекта, который ведёт себя как функция [`int()`](https://python-all.ru/3.10/library/functions.html#int), где аргумент *base* по умолчанию равен двум:235236```python237>>> from functools import partial238>>> basetwo = partial(int, base=2)239>>> basetwo.__doc__ = 'Convert base 2 string to an int.'240>>> basetwo('10010')24118242```243244#### `class functools.partialmethod(func, /, *args, **keywords)`245246Возвращает новый дескриптор [`partialmethod`](https://python-all.ru/3.10/library/functools.html#functools.partialmethod), который ведёт себя как [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial), за исключением того, что он предназначен для использования в качестве определения метода, а не для прямого вызова.247248*func* должен быть [дескриптором](https://python-all.ru/3.10/glossary.html#term-descriptor) или вызываемым объектом (объекты, являющиеся и тем и другим, как обычные функции, обрабатываются как дескрипторы).249250Когда *func* является дескриптором (например, обычная функция Python, [`classmethod()`](https://python-all.ru/3.10/library/functions.html#classmethod), [`staticmethod()`](https://python-all.ru/3.10/library/functions.html#staticmethod), `abstractmethod()` или другой экземпляр [`partialmethod`](https://python-all.ru/3.10/library/functools.html#functools.partialmethod)), вызовы `__get__` делегируются базовому дескриптору, а в качестве результата возвращается подходящий [частичный объект](https://python-all.ru/3.10/library/functools.html#partial-objects).251252Когда *func* – это вызываемый объект, не являющийся дескриптором, динамически создаётся соответствующий связанный метод. Он ведёт себя как обычная функция Python при использовании в качестве метода: аргумент *self* будет вставлен как первый позиционный аргумент, даже до *args* и *keywords*, переданных конструктору [`partialmethod`](https://python-all.ru/3.10/library/functools.html#functools.partialmethod).253254Пример:255256```python257>>> class Cell:258... def __init__(self):259... self._alive = False260... @property261... def alive(self):262... return self._alive263... def set_state(self, state):264... self._alive = bool(state)265... set_alive = partialmethod(set_state, True)266... set_dead = partialmethod(set_state, False)267...268>>> c = Cell()269>>> c.alive270False271>>> c.set_alive()272>>> c.alive273True274```275276Новое в версии 3.4.277278#### `functools.reduce(function, iterable[, initializer])`279280Применяет *функцию* с двумя аргументами последовательно к элементам *итерируемого объекта* слева направо, чтобы свести итерируемый объект к одному значению. Например, `reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` вычисляет `((((1+2)+3)+4)+5)`. Левый аргумент *x* – накопленное значение, а правый аргумент *y* – очередное значение из *итерируемого объекта*. Если указан необязательный *инициализатор*, он помещается перед элементами итерируемого объекта в вычислениях и служит значением по умолчанию, когда итерируемый объект пуст. Если *инициализатор* не указан, а *итерируемый объект* содержит только один элемент, возвращается первый элемент.281282Примерно эквивалентно:283284```python285def reduce(function, iterable, initializer=None):286 it = iter(iterable)287 if initializer is None:288 value = next(it)289 else:290 value = initializer291 for element in it:292 value = function(value, element)293 return value294```295296См. [`itertools.accumulate()`](https://python-all.ru/3.10/library/itertools.html#itertools.accumulate) для итератора, который возвращает все промежуточные значения.297298#### `@functools.singledispatch`299300Преобразует функцию в [однодиспетчерскую](https://python-all.ru/3.10/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.10/glossary.html#term-generic-function).301302Чтобы определить обобщённую функцию, декорируйте её с помощью декоратора `@singledispatch`. При определении функции с помощью `@singledispatch` учтите, что диспетчеризация происходит по типу первого аргумента:303304```python305>>> from functools import singledispatch306>>> @singledispatch307... def fun(arg, verbose=False):308... if verbose:309... print("Let me just say,", end=" ")310... print(arg)311```312313Чтобы добавить перегруженные реализации функции, используйте атрибут `register()` обобщённой функции, который можно использовать как декоратор. Для функций с аннотациями типов декоратор автоматически определяет тип первого аргумента:314315```python316>>> @fun.register317... def _(arg: int, verbose=False):318... if verbose:319... print("Strength in numbers, eh?", end=" ")320... print(arg)321...322>>> @fun.register323... def _(arg: list, verbose=False):324... if verbose:325... print("Enumerate this:")326... for i, elem in enumerate(arg):327... print(i, elem)328```329330Для кода, не использующего аннотации типов, подходящий тип можно передать явно самому декоратору:331332```python333>>> @fun.register(complex)334... def _(arg, verbose=False):335... if verbose:336... print("Better than complicated.", end=" ")337... print(arg.real, arg.imag)338...339```340341Чтобы можно было регистрировать [лямбды](https://python-all.ru/3.10/glossary.html#term-lambda) и уже существующие функции, атрибут `register()` также можно использовать в функциональной форме:342343```python344>>> def nothing(arg, verbose=False):345... print("Nothing.")346...347>>> fun.register(type(None), nothing)348```349350Атрибут `register()` возвращает недекорированную функцию. Это позволяет накладывать декораторы, [`pickling`](https://python-all.ru/3.10/library/pickle.html#module-pickle) и создавать модульные тесты для каждого варианта независимо:351352```python353>>> @fun.register(float)354... @fun.register(Decimal)355... def fun_num(arg, verbose=False):356... if verbose:357... print("Half of your number:", end=" ")358... print(arg / 2)359...360>>> fun_num is fun361False362```363364При вызове обобщённая функция выполняет диспетчеризацию по типу первого аргумента:365366```python367>>> fun("Hello, world.")368Hello, world.369>>> fun("test.", verbose=True)370Let me just say, test.371>>> fun(42, verbose=True)372Strength in numbers, eh? 42373>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)374Enumerate this:3750 spam3761 spam3772 eggs3783 spam379>>> fun(None)380Nothing.381>>> fun(1.23)3820.615383```384385Если для конкретного типа нет зарегистрированной реализации, используется порядок разрешения методов (MRO) этого типа для поиска более общей реализации. Исходная функция, декорированная `@singledispatch`, зарегистрирована для базового типа [`object`](https://python-all.ru/3.10/library/functions.html#object), то есть она используется, если не найдено более подходящей реализации.386387Если реализация зарегистрирована для [абстрактного базового класса](https://python-all.ru/3.10/glossary.html#term-abstract-base-class), виртуальные подклассы этого базового класса будут диспетчеризоваться к этой реализации:388389```python390>>> from collections.abc import Mapping391>>> @fun.register392... def _(arg: Mapping, verbose=False):393... if verbose:394... print("Keys & Values")395... for key, value in arg.items():396... print(key, "=>", value)397...398>>> fun({"a": "b"})399a => b400```401402Чтобы проверить, какую реализацию выберет обобщённая функция для заданного типа, используйте атрибут `dispatch()`:403404```python405>>> fun.dispatch(float)406<function fun_num at 0x1035a2840>407>>> fun.dispatch(dict) # примечание: реализация по умолчанию408<function fun at 0x103fe0000>409```410411Чтобы получить доступ ко всем зарегистрированным реализациям, используйте атрибут `registry` только для чтения:412413```python414>>> fun.registry.keys()415dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,416 <class 'decimal.Decimal'>, <class 'list'>,417 <class 'float'>])418>>> fun.registry[float]419<function fun_num at 0x1035a2840>420>>> fun.registry[object]421<function fun at 0x103fe0000>422```423424Новое в версии 3.4.425426Изменено в версии 3.7: Атрибут `register()` теперь поддерживает использование аннотаций типов.427428#### `class functools.singledispatchmethod(func)`429430Преобразует метод в [однодиспетчерскую](https://python-all.ru/3.10/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.10/glossary.html#term-generic-function).431432Чтобы определить обобщённый метод, декорируйте его декоратором `@singledispatchmethod`. При определении функции с помощью `@singledispatchmethod` учтите, что диспетчеризация выполняется по типу первого аргумента, отличного от *self* или *cls*:433434```python435class Negator:436 @singledispatchmethod437 def neg(self, arg):438 raise NotImplementedError("Cannot negate a")439440 @neg.register441 def _(self, arg: int):442 return -arg443444 @neg.register445 def _(self, arg: bool):446 return not arg447```448449`@singledispatchmethod` поддерживает вложение с другими декораторами, такими как [`@classmethod`](https://python-all.ru/3.10/library/functions.html#classmethod). Обратите внимание, что для поддержки `dispatcher.register` `singledispatchmethod` должен быть *самым внешним* декоратором. Вот класс `Negator` с методами `neg`, привязанными к классу, а не к экземпляру класса:450451```python452class Negator:453 @singledispatchmethod454 @classmethod455 def neg(cls, arg):456 raise NotImplementedError("Cannot negate a")457458 @neg.register459 @classmethod460 def _(cls, arg: int):461 return -arg462463 @neg.register464 @classmethod465 def _(cls, arg: bool):466 return not arg467```468469Этот же подход можно использовать для других подобных декораторов: [`@staticmethod`](https://python-all.ru/3.10/library/functions.html#staticmethod), [`@abstractmethod`](https://python-all.ru/3.10/library/abc.html#abc.abstractmethod) и других.470471Новое в версии 3.8.472473#### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`474475Обновляет функцию-*обёртку* так, чтобы она походила на *обёрнутую* функцию. Необязательные аргументы – это кортежи, определяющие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки, а какие атрибуты функции-обёртки обновляются соответствующими атрибутами из исходной функции. Значения по умолчанию для этих аргументов – константы уровня модуля `WRAPPER_ASSIGNMENTS` (которая присваивает функции-обёртке атрибуты `__module__`, `__name__`, `__qualname__`, `__annotations__` и `__doc__`, строку документации) и `WRAPPER_UPDATES` (которая обновляет `__dict__` функции-обёртки, т.е. словарь экземпляра).476477Чтобы обеспечить доступ к исходной функции для интроспекции и других целей (например, обхода кэширующего декоратора такого как [`lru_cache()`](https://python-all.ru/3.10/library/functools.html#functools.lru_cache)), эта функция автоматически добавляет атрибут `__wrapped__` к обёртке, который ссылается на обёрнутую функцию.478479Основное назначение этой функции – использование в [decorator](https://python-all.ru/3.10/glossary.html#term-decorator)-функциях, которые оборачивают декорированную функцию и возвращают обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно не очень полезно.480481[`update_wrapper()`](https://python-all.ru/3.10/library/functools.html#functools.update_wrapper) можно использовать с вызываемыми объектами, отличными от функций. Любые атрибуты, указанные в *assigned* или *updated*, отсутствующие в оборачиваемом объекте, игнорируются (т.е. эта функция не будет пытаться установить их в функции-обёртке). [`AttributeError`](https://python-all.ru/3.10/library/exceptions.html#AttributeError) по-прежнему возбуждается, если сама функция-обёртка не имеет каких-либо атрибутов, перечисленных в *updated*.482483Новое в версии 3.2: Автоматическое добавление атрибута `__wrapped__`.484485Новое в версии 3.2: Копирование атрибута `__annotations__` по умолчанию.486487Изменено в версии 3.2: Отсутствующие атрибуты больше не вызывают исключение [`AttributeError`](https://python-all.ru/3.10/library/exceptions.html#AttributeError).488489Изменено в версии 3.4: Атрибут `__wrapped__` теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут `__wrapped__`. (см. [bpo-17482](https://python-all.ru/3.10/library/functools.html))490491#### `@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`492493Это удобная функция для вызова [`update_wrapper()`](https://python-all.ru/3.10/library/functools.html#functools.update_wrapper) в качестве декоратора функции при определении функции-обёртки. Она эквивалентна `partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`. Например:494495```python496>>> from functools import wraps497>>> def my_decorator(f):498... @wraps(f)499... def wrapper(*args, **kwds):500... print('Calling decorated function')501... return f(*args, **kwds)502... return wrapper503...504>>> @my_decorator505... def example():506... """докстринг"""507... print('Called example function')508...509>>> example()510Calling decorated function511Called example function512>>> example.__name__513'example'514>>> example.__doc__515'Docstring'516```517518Без использования этой фабрики декораторов имя примера функции было бы `'wrapper'`, а строка документации исходной `example()` была бы потеряна.519520## [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial) Объекты521522Объекты [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial) – это вызываемые объекты, создаваемые [`partial()`](https://python-all.ru/3.10/library/functools.html#functools.partial). Они имеют три атрибута только для чтения:523524#### `partial.func`525526Вызываемый объект или функция. Вызовы объекта [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial) будут перенаправлены в [`func`](https://python-all.ru/3.10/library/functools.html#functools.partial.func) с новыми аргументами и ключевыми словами.527528#### `partial.args`529530Крайние левые позиционные аргументы, которые будут добавлены перед позиционными аргументами, переданными при вызове объекта [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial).531532#### `partial.keywords`533534Ключевые аргументы, которые будут предоставлены при вызове объекта [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial).535536Объекты [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial) похожи на объекты `function` тем, что они вызываемы, могут быть слабыми ссылками и могут иметь атрибуты. Есть несколько важных отличий. Например, атрибуты [`__name__`](https://python-all.ru/3.10/library/stdtypes.html#definition.__name__) и `__doc__` не создаются автоматически. Кроме того, объекты [`partial`](https://python-all.ru/3.10/library/functools.html#functools.partial), определённые в классах, ведут себя как статические методы и не превращаются в связанные методы при поиске атрибута экземпляра.537