functools.md
1> **Источник:** https://python-all.ru/3.9/library/functools.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`functools`](https://python-all.ru/3.9/library/functools.html#module-functools) – Функции высшего порядка и операции над вызываемыми объектами89**Исходный код:** [Lib/functools.py](https://python-all.ru/src/3.9/Lib/functools.py)1011---1213Модуль [`functools`](https://python-all.ru/3.9/library/functools.html#module-functools) предназначен для функций высшего порядка: функций, которые действуют на или возвращают другие функции. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.1415Модуль [`functools`](https://python-all.ru/3.9/library/functools.html#module-functools) определяет следующие функции:1617#### `@functools.cache(user_function)`1819Простой легковесный кэш функций без ограничения размера. Иногда называется [«мемоизация»](https://python-all.ru/3.9/library/functools.html).2021Возвращает то же, что и `lru_cache(maxsize=None)`, создавая тонкую обёртку вокруг поиска по словарю для аргументов функции. Поскольку ему никогда не нужно вытеснять старые значения, он меньше и быстрее, чем [`lru_cache()`](https://python-all.ru/3.9/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.9/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.9/library/functools.html#functools.cached_property) несколько отличается от [`property()`](https://python-all.ru/3.9/library/functions.html#property). Обычное свойство блокирует запись атрибута, если не определён сеттер. Напротив, *cached\_property* разрешает запись.5859Декоратор *cached\_property* выполняется только при поиске атрибута и только когда атрибут с таким именем не существует. Когда он выполняется, *cached\_property* записывает значение в атрибут с тем же именем. Последующие чтения и записи атрибута имеют приоритет над методом *cached\_property*, и он работает как обычный атрибут.6061Кэшированное значение можно очистить, удалив атрибут. Это позволяет методу *cached\_property* выполниться снова.6263Примечание: этот декоратор мешает работе [**PEP 412**](https://python-all.ru/3.9/library/functools.html) словарей с разделением ключей. Это означает, что словари экземпляров могут занимать больше места, чем обычно.6465Кроме того, этот декоратор требует, чтобы атрибут `__dict__` на каждом экземпляре был изменяемым отображением. Это означает, что он не будет работать с некоторыми типами, такими как метаклассы (поскольку атрибуты `__dict__` на экземплярах типа являются прокси только для чтения пространства имён класса), и теми, которые указывают `__slots__` без включения `__dict__` в качестве одного из определённых слотов (так как такие классы вообще не предоставляют атрибут `__dict__`).6667Если изменяемое отображение недоступно или требуется экономное по памяти совместное использование ключей, эффект, подобный [`cached_property()`](https://python-all.ru/3.9/library/functools.html#functools.cached_property), можно получить, разместив [`property()`](https://python-all.ru/3.9/library/functions.html#property) поверх [`cache()`](https://python-all.ru/3.9/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.9/glossary.html#term-key-function). Используется с инструментами, которые принимают функции ключа (такими как [`sorted()`](https://python-all.ru/3.9/library/functions.html#sorted), [`min()`](https://python-all.ru/3.9/library/functions.html#min), [`max()`](https://python-all.ru/3.9/library/functions.html#max), [`heapq.nlargest()`](https://python-all.ru/3.9/library/heapq.html#heapq.nlargest), [`heapq.nsmallest()`](https://python-all.ru/3.9/library/heapq.html#heapq.nsmallest), [`itertools.groupby()`](https://python-all.ru/3.9/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.9/howto/sorting.html#sortinghowto).9596Новое в версии 3.2.9798#### `@functools.lru_cache(user_function)`99100#### `@functools.lru_cache(maxsize=128, typed=False)`101102Декоратор для обёртывания функции запоминающим вызываемым объектом, который сохраняет до *maxsize* последних вызовов. Это может сэкономить время, когда дорогостоящая или связанная с вводом-выводом функция периодически вызывается с одними и теми же аргументами.103104Поскольку для кеширования результатов используется словарь, позиционные и именованные аргументы функции должны быть хешируемыми.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 sentence = sentence.casefold()114 return sum(sentence.count(vowel) for vowel in 'aeiou')115```116117Если *maxsize* установлен в `None`, механизм LRU отключается и кэш может расти без ограничений.118119Если для *typed* установлено значение true, аргументы функции разных типов будут кешироваться отдельно. Например, `f(3)` и `f(3.0)` будут считаться разными вызовами с разными результатами.120121Обёрнутая функция оснащается `cache_parameters()` функцией, которая возвращает новый [`dict`](https://python-all.ru/3.9/library/stdtypes.html#dict), отображающий значения *maxsize* и *typed*. Это только для информационных целей. Изменение значений не влияет на поведение.122123Чтобы оценить эффективность кеша и настроить параметр *maxsize*, обёрнутая функция дополняется функцией `cache_info()`, которая возвращает [именованный кортеж](https://python-all.ru/3.9/glossary.html#term-named-tuple), показывающий *hits* (попадания), *misses* (промахи), *maxsize* и *currsize*. В многопоточной среде значения попаданий и промахов являются приблизительными.124125Декоратор также предоставляет функцию `cache_clear()` для очистки или инвалидации кэша.126127Исходная базовая функция доступна через атрибут `__wrapped__`. Это полезно для интроспекции, обхода кэша или повторной обёртки функции другим кэшем.128129[LRU-кэш (вытесняющий давно неиспользовавшиеся элементы)](https://python-all.ru/3.9/library/functools.html) работает лучше всего, когда самые недавние вызовы являются наилучшими предикторами будущих вызовов (например, самые популярные статьи на новостном сервере обычно меняются каждый день). Ограничение размера кэша гарантирует, что кэш не будет расти бесконечно в долго работающих процессах, таких как веб-серверы.130131В целом, LRU-кеш следует использовать только тогда, когда нужно повторно использовать ранее вычисленные значения. Соответственно, нет смысла кешировать функции с побочными эффектами, функции, которым нужно создавать различные изменяемые объекты при каждом вызове, или нечистые функции, такие как time() или random().132133Пример LRU-кэша для статического веб-контента:134135```python136@lru_cache(maxsize=32)137def get_pep(num):138 'Retrieve text of a Python Enhancement Proposal'139 resource = 'https://www.python.org/dev/peps/pep-%04d/' % num140 try:141 with urllib.request.urlopen(resource) as s:142 return s.read()143 except urllib.error.HTTPError:144 return 'Not Found'145146>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:147... pep = get_pep(n)148... print(n, len(pep))149150>>> get_pep.cache_info()151CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)152```153154Пример эффективного вычисления [чисел Фибоначчи](https://python-all.ru/3.9/library/functools.html) с использованием кэша для реализации техники [динамического программирования](https://python-all.ru/3.9/library/functools.html):155156```python157@lru_cache(maxsize=None)158def fib(n):159 if n < 2:160 return n161 return fib(n-1) + fib(n-2)162163>>> [fib(n) for n in range(16)]164[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]165166>>> fib.cache_info()167CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)168```169170Новое в версии 3.2.171172Изменено в версии 3.3: Добавлена опция *typed*.173174Изменено в версии 3.8: Добавлена опция *user\_function*.175176Новое в версии 3.9: Добавлена функция `cache_parameters()`177178#### `@functools.total_ordering`179180Для класса, определяющего один или несколько методов расширенного сравнения, этот декоратор класса предоставляет остальные. Это упрощает задачу определения всех возможных операций расширенного сравнения:181182Класс должен определять один из [`__lt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__gt__) или [`__ge__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__ge__). Кроме того, класс должен предоставлять метод [`__eq__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__eq__).183184Например:185186```python187@total_ordering188class Student:189 def _is_valid_operand(self, other):190 return (hasattr(other, "lastname") and191 hasattr(other, "firstname"))192 def __eq__(self, other):193 if not self._is_valid_operand(other):194 return NotImplemented195 return ((self.lastname.lower(), self.firstname.lower()) ==196 (other.lastname.lower(), other.firstname.lower()))197 def __lt__(self, other):198 if not self._is_valid_operand(other):199 return NotImplemented200 return ((self.lastname.lower(), self.firstname.lower()) <201 (other.lastname.lower(), other.firstname.lower()))202```203204> **Примечание**205>206> Хотя этот декоратор упрощает создание хорошо ведущих себя полностью упорядоченных типов, он *действительно* приводит к более медленному выполнению и более сложным трассировкам стека для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для конкретного приложения, реализация всех шести методов расширенного сравнения вместо этого, вероятно, даст лёгкий прирост скорости.207208Новое в версии 3.2.209210Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из нижележащей функции сравнения для нераспознанных типов.211212#### `functools.partial(func, /, *args, **keywords)`213214Возвращает новый [объект partial](https://python-all.ru/3.9/library/functools.html#partial-objects), который при вызове будет вести себя как *func*, вызванная с позиционными аргументами *args* и именованными аргументами *keywords*. Если при вызове передаётся больше аргументов, они добавляются к *args*. Если передаются дополнительные именованные аргументы, они расширяют и переопределяют *keywords*. Примерно эквивалентно следующему:215216```python217def partial(func, /, *args, **keywords):218 def newfunc(*fargs, **fkeywords):219 newkeywords = {**keywords, **fkeywords}220 return func(*args, *fargs, **newkeywords)221 newfunc.func = func222 newfunc.args = args223 newfunc.keywords = keywords224 return newfunc225```226227[`partial()`](https://python-all.ru/3.9/library/functools.html#functools.partial) используется для частичного применения функции, которое «замораживает» некоторую часть аргументов и/или ключевых слов функции, создавая новый объект с упрощённой сигнатурой. Например, [`partial()`](https://python-all.ru/3.9/library/functools.html#functools.partial) можно использовать для создания вызываемого объекта, который ведёт себя как функция [`int()`](https://python-all.ru/3.9/library/functions.html#int), где аргумент *base* по умолчанию равен двум:228229```python230>>> from functools import partial231>>> basetwo = partial(int, base=2)232>>> basetwo.__doc__ = 'Convert base 2 string to an int.'233>>> basetwo('10010')23418235```236237#### `class functools.partialmethod(func, /, *args, **keywords)`238239Возвращает новый дескриптор [`partialmethod`](https://python-all.ru/3.9/library/functools.html#functools.partialmethod), который ведёт себя как [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial), за исключением того, что он предназначен для использования в качестве определения метода, а не для прямого вызова.240241*func* должен быть [дескриптором](https://python-all.ru/3.9/glossary.html#term-descriptor) или вызываемым объектом (объекты, являющиеся и тем и другим, как обычные функции, обрабатываются как дескрипторы).242243Когда *func* является дескриптором (например, обычная функция Python, [`classmethod()`](https://python-all.ru/3.9/library/functions.html#classmethod), [`staticmethod()`](https://python-all.ru/3.9/library/functions.html#staticmethod), `abstractmethod()` или другой экземпляр [`partialmethod`](https://python-all.ru/3.9/library/functools.html#functools.partialmethod)), вызовы `__get__` делегируются базовому дескриптору, а в качестве результата возвращается подходящий [частичный объект](https://python-all.ru/3.9/library/functools.html#partial-objects).244245Когда *func* – это вызываемый объект, не являющийся дескриптором, динамически создаётся соответствующий связанный метод. Он ведёт себя как обычная функция Python при использовании в качестве метода: аргумент *self* будет вставлен как первый позиционный аргумент, даже до *args* и *keywords*, переданных конструктору [`partialmethod`](https://python-all.ru/3.9/library/functools.html#functools.partialmethod).246247Пример:248249```python250>>> class Cell:251... def __init__(self):252... self._alive = False253... @property254... def alive(self):255... return self._alive256... def set_state(self, state):257... self._alive = bool(state)258... set_alive = partialmethod(set_state, True)259... set_dead = partialmethod(set_state, False)260...261>>> c = Cell()262>>> c.alive263False264>>> c.set_alive()265>>> c.alive266True267```268269Новое в версии 3.4.270271#### `functools.reduce(function, iterable[, initializer])`272273Применяет *функцию* с двумя аргументами последовательно к элементам *итерируемого объекта* слева направо, чтобы свести итерируемый объект к одному значению. Например, `reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` вычисляет `((((1+2)+3)+4)+5)`. Левый аргумент *x* – накопленное значение, а правый аргумент *y* – очередное значение из *итерируемого объекта*. Если указан необязательный *инициализатор*, он помещается перед элементами итерируемого объекта в вычислениях и служит значением по умолчанию, когда итерируемый объект пуст. Если *инициализатор* не указан, а *итерируемый объект* содержит только один элемент, возвращается первый элемент.274275Примерно эквивалентно:276277```python278def reduce(function, iterable, initializer=None):279 it = iter(iterable)280 if initializer is None:281 value = next(it)282 else:283 value = initializer284 for element in it:285 value = function(value, element)286 return value287```288289См. [`itertools.accumulate()`](https://python-all.ru/3.9/library/itertools.html#itertools.accumulate) для итератора, который возвращает все промежуточные значения.290291#### `@functools.singledispatch`292293Преобразует функцию в [однодиспетчерскую](https://python-all.ru/3.9/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.9/glossary.html#term-generic-function).294295Чтобы определить обобщённую функцию, декорируйте её с помощью декоратора `@singledispatch`. При определении функции с помощью `@singledispatch` учтите, что диспетчеризация происходит по типу первого аргумента:296297```python298>>> from functools import singledispatch299>>> @singledispatch300... def fun(arg, verbose=False):301... if verbose:302... print("Let me just say,", end=" ")303... print(arg)304```305306Чтобы добавить перегруженные реализации функции, используйте атрибут `register()` обобщённой функции, который можно использовать как декоратор. Для функций с аннотациями типов декоратор автоматически определяет тип первого аргумента:307308```python309>>> @fun.register310... def _(arg: int, verbose=False):311... if verbose:312... print("Strength in numbers, eh?", end=" ")313... print(arg)314...315>>> @fun.register316... def _(arg: list, verbose=False):317... if verbose:318... print("Enumerate this:")319... for i, elem in enumerate(arg):320... print(i, elem)321```322323Для кода, не использующего аннотации типов, подходящий тип можно передать явно самому декоратору:324325```python326>>> @fun.register(complex)327... def _(arg, verbose=False):328... if verbose:329... print("Better than complicated.", end=" ")330... print(arg.real, arg.imag)331...332```333334Чтобы можно было регистрировать [лямбды](https://python-all.ru/3.9/glossary.html#term-lambda) и уже существующие функции, атрибут `register()` также можно использовать в функциональной форме:335336```python337>>> def nothing(arg, verbose=False):338... print("Nothing.")339...340>>> fun.register(type(None), nothing)341```342343Атрибут `register()` возвращает недекорированную функцию. Это позволяет накладывать декораторы, [`pickling`](https://python-all.ru/3.9/library/pickle.html#module-pickle) и создавать модульные тесты для каждого варианта независимо:344345```python346>>> @fun.register(float)347... @fun.register(Decimal)348... def fun_num(arg, verbose=False):349... if verbose:350... print("Half of your number:", end=" ")351... print(arg / 2)352...353>>> fun_num is fun354False355```356357При вызове обобщённая функция выполняет диспетчеризацию по типу первого аргумента:358359```python360>>> fun("Hello, world.")361Hello, world.362>>> fun("test.", verbose=True)363Let me just say, test.364>>> fun(42, verbose=True)365Strength in numbers, eh? 42366>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)367Enumerate this:3680 spam3691 spam3702 eggs3713 spam372>>> fun(None)373Nothing.374>>> fun(1.23)3750.615376```377378Если для конкретного типа нет зарегистрированной реализации, используется порядок разрешения методов (MRO) этого типа для поиска более общей реализации. Исходная функция, декорированная `@singledispatch`, зарегистрирована для базового типа [`object`](https://python-all.ru/3.9/library/functions.html#object), то есть она используется, если не найдено более подходящей реализации.379380Если реализация зарегистрирована для [абстрактного базового класса](https://python-all.ru/3.9/glossary.html#term-abstract-base-class), виртуальные подклассы этого базового класса будут диспетчеризоваться к этой реализации:381382```python383>>> from collections.abc import Mapping384>>> @fun.register385... def _(arg: Mapping, verbose=False):386... if verbose:387... print("Keys & Values")388... for key, value in arg.items():389... print(key, "=>", value)390...391>>> fun({"a": "b"})392a => b393```394395Чтобы проверить, какую реализацию выберет обобщённая функция для заданного типа, используйте атрибут `dispatch()`:396397```python398>>> fun.dispatch(float)399<function fun_num at 0x1035a2840>400>>> fun.dispatch(dict) # примечание: реализация по умолчанию401<function fun at 0x103fe0000>402```403404Чтобы получить доступ ко всем зарегистрированным реализациям, используйте атрибут `registry` только для чтения:405406```python407>>> fun.registry.keys()408dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,409 <class 'decimal.Decimal'>, <class 'list'>,410 <class 'float'>])411>>> fun.registry[float]412<function fun_num at 0x1035a2840>413>>> fun.registry[object]414<function fun at 0x103fe0000>415```416417Новое в версии 3.4.418419Изменено в версии 3.7: Атрибут `register()` теперь поддерживает использование аннотаций типов.420421#### `class functools.singledispatchmethod(func)`422423Преобразует метод в [однодиспетчерскую](https://python-all.ru/3.9/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.9/glossary.html#term-generic-function).424425Чтобы определить обобщённый метод, декорируйте его декоратором `@singledispatchmethod`. При определении функции с помощью `@singledispatchmethod` учтите, что диспетчеризация выполняется по типу первого аргумента, отличного от *self* или *cls*:426427```python428class Negator:429 @singledispatchmethod430 def neg(self, arg):431 raise NotImplementedError("Cannot negate a")432433 @neg.register434 def _(self, arg: int):435 return -arg436437 @neg.register438 def _(self, arg: bool):439 return not arg440```441442`@singledispatchmethod` поддерживает вложение с другими декораторами, такими как [`@classmethod`](https://python-all.ru/3.9/library/functions.html#classmethod). Обратите внимание, что для поддержки `dispatcher.register` `singledispatchmethod` должен быть *самым внешним* декоратором. Вот класс `Negator` с методами `neg`, привязанными к классу, а не к экземпляру класса:443444```python445class Negator:446 @singledispatchmethod447 @classmethod448 def neg(cls, arg):449 raise NotImplementedError("Cannot negate a")450451 @neg.register452 @classmethod453 def _(cls, arg: int):454 return -arg455456 @neg.register457 @classmethod458 def _(cls, arg: bool):459 return not arg460```461462Этот же подход можно использовать для других подобных декораторов: [`@staticmethod`](https://python-all.ru/3.9/library/functions.html#staticmethod), [`@abstractmethod`](https://python-all.ru/3.9/library/abc.html#abc.abstractmethod) и других.463464Новое в версии 3.8.465466#### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`467468Обновляет функцию-*обёртку* так, чтобы она походила на *обёрнутую* функцию. Необязательные аргументы – это кортежи, определяющие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки, а какие атрибуты функции-обёртки обновляются соответствующими атрибутами из исходной функции. Значения по умолчанию для этих аргументов – константы уровня модуля `WRAPPER_ASSIGNMENTS` (которая присваивает функции-обёртке атрибуты `__module__`, `__name__`, `__qualname__`, `__annotations__` и `__doc__`, строку документации) и `WRAPPER_UPDATES` (которая обновляет `__dict__` функции-обёртки, т.е. словарь экземпляра).469470Чтобы обеспечить доступ к исходной функции для интроспекции и других целей (например, обхода кэширующего декоратора такого как [`lru_cache()`](https://python-all.ru/3.9/library/functools.html#functools.lru_cache)), эта функция автоматически добавляет атрибут `__wrapped__` к обёртке, который ссылается на обёрнутую функцию.471472Основное назначение этой функции – использование в [decorator](https://python-all.ru/3.9/glossary.html#term-decorator)-функциях, которые оборачивают декорированную функцию и возвращают обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно не очень полезно.473474[`update_wrapper()`](https://python-all.ru/3.9/library/functools.html#functools.update_wrapper) можно использовать с вызываемыми объектами, отличными от функций. Любые атрибуты, указанные в *assigned* или *updated*, отсутствующие в оборачиваемом объекте, игнорируются (т.е. эта функция не будет пытаться установить их в функции-обёртке). [`AttributeError`](https://python-all.ru/3.9/library/exceptions.html#AttributeError) по-прежнему возбуждается, если сама функция-обёртка не имеет каких-либо атрибутов, перечисленных в *updated*.475476Новое в версии 3.2: Автоматическое добавление атрибута `__wrapped__`.477478Новое в версии 3.2: Копирование атрибута `__annotations__` по умолчанию.479480Изменено в версии 3.2: Отсутствующие атрибуты больше не вызывают исключение [`AttributeError`](https://python-all.ru/3.9/library/exceptions.html#AttributeError).481482Изменено в версии 3.4: Атрибут `__wrapped__` теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут `__wrapped__`. (см. [bpo-17482](https://python-all.ru/3.9/library/functools.html))483484#### `@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`485486Это удобная функция для вызова [`update_wrapper()`](https://python-all.ru/3.9/library/functools.html#functools.update_wrapper) в качестве декоратора функции при определении функции-обёртки. Она эквивалентна `partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`. Например:487488```python489>>> from functools import wraps490>>> def my_decorator(f):491... @wraps(f)492... def wrapper(*args, **kwds):493... print('Calling decorated function')494... return f(*args, **kwds)495... return wrapper496...497>>> @my_decorator498... def example():499... """докстринг"""500... print('Called example function')501...502>>> example()503Calling decorated function504Called example function505>>> example.__name__506'example'507>>> example.__doc__508'Docstring'509```510511Без использования этой фабрики декораторов имя примера функции было бы `'wrapper'`, а строка документации исходной `example()` была бы потеряна.512513## [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial) Объекты514515Объекты [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial) – это вызываемые объекты, создаваемые [`partial()`](https://python-all.ru/3.9/library/functools.html#functools.partial). Они имеют три атрибута только для чтения:516517#### `partial.func`518519Вызываемый объект или функция. Вызовы объекта [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial) будут перенаправлены в [`func`](https://python-all.ru/3.9/library/functools.html#functools.partial.func) с новыми аргументами и ключевыми словами.520521#### `partial.args`522523Крайние левые позиционные аргументы, которые будут добавлены перед позиционными аргументами, переданными при вызове объекта [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial).524525#### `partial.keywords`526527Ключевые аргументы, которые будут предоставлены при вызове объекта [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial).528529Объекты [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial) похожи на объекты `function` тем, что они вызываемы, могут быть слабыми ссылками и могут иметь атрибуты. Есть несколько важных отличий. Например, атрибуты [`__name__`](https://python-all.ru/3.9/library/stdtypes.html#definition.__name__) и `__doc__` не создаются автоматически. Кроме того, объекты [`partial`](https://python-all.ru/3.9/library/functools.html#functools.partial), определённые в классах, ведут себя как статические методы и не превращаются в связанные методы при поиске атрибута экземпляра.530