Документация Python неофициальный перевод
Содержание страницы

functools – Функции высшего порядка и операции над вызываемыми объектамиfunctools – Higher-order functions and operations on callable objects

Исходный код: Lib/functools.py


Модуль functools предназначен для функций высшего порядка: функций, которые действуют на или возвращают другие функции. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.

Модуль functools определяет следующие функции:

@functools.cache(user_function)

Простой легковесный кэш функций без ограничения размера. Иногда называется «мемоизация».

Возвращает то же, что и lru_cache(maxsize=None), создавая тонкую обёртку вокруг поиска по словарю для аргументов функции. Поскольку ему никогда не нужно вытеснять старые значения, он меньше и быстрее, чем lru_cache() с ограничением размера.

Например:

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1

>>> factorial(10)   # результат ранее не кэшировался, выполняется 11 рекурсивных вызовов
3628800
>>> factorial(5)    # нет новых вызовов, просто возвращается кэшированный результат
120
>>> factorial(12)   # два новых рекурсивных вызова, factorial(10) кэширован
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 выполниться снова.

cached_property не предотвращает возможное состояние гонки при многопоточном использовании. Функция геттер может выполниться более одного раза на одном и том же экземпляре, при этом последний запуск устанавливает кэшированное значение. Если кэшированное свойство идемпотентно или его повторное выполнение на экземпляре не вредно, это нормально. Если требуется синхронизация, реализуйте необходимую блокировку внутри декорированной функции геттера или вокруг доступа к кэшированному свойству.

Примечание: этот декоратор мешает работе PEP 412 словарей с разделением ключей. Это означает, что словари экземпляров могут занимать больше места, чем обычно.

Кроме того, этот декоратор требует, чтобы атрибут __dict__ на каждом экземпляре был изменяемым отображением. Это означает, что он не будет работать с некоторыми типами, такими как метаклассы (поскольку атрибуты __dict__ на экземплярах типа являются прокси только для чтения пространства имён класса), и теми, которые указывают __slots__ без включения __dict__ в качестве одного из определённых слотов (так как такие классы вообще не предоставляют атрибут __dict__).

Если изменяемое отображение недоступно или требуется эффективное по памяти разделение ключей, эффекта, аналогичного cached_property(), можно также достичь, разместив property() поверх lru_cache(). Смотрите Как кэшировать вызовы методов? для получения дополнительных сведений о том, чем это отличается от cached_property().

Добавлено в версии 3.8.

Изменено в версии 3.12: До Python 3.12 cached_property содержал недокументированную блокировку, чтобы гарантировать, что при многопоточном использовании функция-геттер будет выполняться только один раз на экземпляр. Однако блокировка была по свойству, а не по экземпляру, что могло приводить к неприемлемо высокой конкуренции за блокировку. Начиная с Python 3.12 эта блокировка удалена.

functools.cmp_to_key(func)

Преобразует старую функцию сравнения в функцию ключа. Используется с инструментами, которые принимают функции ключа (такими как sorted(), min(), max(), heapq.nlargest(), heapq.nsmallest(), itertools.groupby()). Эта функция в первую очередь используется как инструмент перехода для программ, переносимых с Python 2, который поддерживал использование функций сравнения.

Функция сравнения – это любой вызываемый объект, который принимает два аргумента, сравнивает их и возвращает отрицательное число для «меньше», ноль для «равно» или положительное число для «больше». Функция ключа – это вызываемый объект, который принимает один аргумент и возвращает другое значение, используемое как ключ сортировки.

Пример:

sorted(iterable, key=cmp_to_key(locale.strcoll))  # порядок сортировки с учётом локали

Примеры сортировки и краткое руководство по сортировке см. в Методы сортировки.

Добавлено в версии 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(word):
    return sum(word.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() функцией, которая возвращает именованный кортеж, показывающий hits, misses, maxsize и currsize.

Декоратор также предоставляет функцию cache_clear() для очистки или инвалидации кэша.

Исходная базовая функция доступна через атрибут __wrapped__. Это полезно для интроспекции, обхода кэша или повторной обёртки функции другим кэшем.

Кэш хранит ссылки на аргументы и возвращаемые значения до тех пор, пока они не устареют и не будут вытеснены из кэша, или пока кэш не будет очищен.

Если кэшируется метод, аргумент экземпляра self также попадает в кэш. См. Как кэшировать вызовы методов?

LRU-кэш (вытесняющий давно неиспользовавшиеся элементы) работает лучше всего, когда самые недавние вызовы являются наилучшими предикторами будущих вызовов (например, самые популярные статьи на новостном сервере обычно меняются каждый день). Ограничение размера кэша гарантирует, что кэш не будет расти бесконечно в долго работающих процессах, таких как веб-серверы.

В целом, LRU-кэш следует использовать только тогда, когда нужно повторно использовать ранее вычисленные значения. Соответственно, не имеет смысла кэшировать функции с побочными эффектами, функции, которые должны создавать различные изменяемые объекты при каждом вызове (например, генераторы и асинхронные функции), или нечистые функции, такие как time() или random().

Пример LRU-кэша для статического веб-контента:

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = f'https://peps.python.org/pep-{num:04d}'
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
...     pep = get_pep(n)
...     print(n, len(pep))

>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

Пример эффективного вычисления чисел Фибоначчи с использованием кэша для реализации техники динамического программирования:

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

Добавлено в версии 3.2.

Изменено в версии 3.3: Добавлена опция typed.

Изменено в версии 3.8: Добавлена опция user_function.

Изменено в версии 3.9: Добавлена функция cache_parameters()

@functools.total_ordering

Для класса, определяющего один или несколько методов расширенного сравнения, этот декоратор класса предоставляет остальные. Это упрощает задачу определения всех возможных операций расширенного сравнения:

Класс должен определять один из __lt__(), __le__(), __gt__() или __ge__(). Кроме того, класс должен предоставлять метод __eq__().

Например:

@total_ordering
class Student:
    def _is_valid_operand(self, other):
        return (hasattr(other, "lastname") and
                hasattr(other, "firstname"))
    def __eq__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

Примечание

Хотя этот декоратор упрощает создание хорошо ведущих себя полностью упорядоченных типов, он действительно приводит к более медленному выполнению и более сложным трассировкам стека для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для конкретного приложения, реализация всех шести методов расширенного сравнения вместо этого, вероятно, даст лёгкий прирост скорости.

Примечание

Этот декоратор не пытается переопределить методы, объявленные в классе или его суперклассах. Это означает, что если суперкласс определяет оператор сравнения, total_ordering не будет реализовывать его снова, даже если исходный метод является абстрактным.

Добавлено в версии 3.2.

Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из базовой функции сравнения для нераспознанных типов.

functools.Placeholder

Объект-одиночка, используемый в качестве стража для резервирования места для позиционных аргументов при вызове partial() и partialmethod().

Добавлено в версии 3.14.

functools.partial(func, /, *args, **keywords)

Возвращает новый объект partial, который при вызове будет вести себя как func, вызванная с позиционными аргументами args и именованными аргументами keywords. Если при вызове передаётся больше аргументов, они добавляются к args. Если передаются дополнительные именованные аргументы, они расширяют и переопределяют keywords. Примерно эквивалентно следующему:

def partial(func, /, *args, **keywords):
    def newfunc(*more_args, **more_keywords):
        return func(*args, *more_args, **(keywords | more_keywords))
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

Функция partial() используется для частичного применения функции, которое «замораживает» некоторую часть аргументов и/или именованных аргументов функции, создавая новый объект с упрощённой сигнатурой. Например, partial() можно использовать для создания вызываемого объекта, который ведёт себя как функция int(), у которой аргумент base по умолчанию равен 2:

>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18

Если в args присутствуют сторожевые значения Placeholder, они будут заполнены в первую очередь при вызове partial(). Это позволяет предварительно заполнить любой позиционный аргумент с помощью вызова partial(); без Placeholder можно предварительно заполнить только выбранное количество начальных позиционных аргументов.

Если присутствуют какие-либо сторожевые значения Placeholder, все они должны быть заполнены во время вызова:

>>> say_to_world = partial(print, Placeholder, Placeholder, "world!")
>>> say_to_world('Hello', 'dear')
Hello dear world!

Вызов say_to_world('Hello') вызывает TypeError, потому что предоставлен только один позиционный аргумент, но есть два заполнителя, которые необходимо заполнить.

Если partial() применяется к существующему объекту partial(), сторожевые значения Placeholder входного объекта заполняются новыми позиционными аргументами. Заполнитель можно сохранить, вставив новый сторож Placeholder на место, занимаемое предыдущим Placeholder:

>>> from functools import partial, Placeholder as _
>>> remove = partial(str.replace, _, _, '')
>>> message = 'Hello, dear dear world!'
>>> remove(message, ' dear')
'Hello, world!'
>>> remove_dear = partial(remove, _, ' dear')
>>> remove_dear(message)
'Hello, world!'
>>> remove_first_dear = partial(remove_dear, _, 1)
>>> remove_first_dear(message)
'Hello, dear world!'

Placeholder нельзя передать в partial() как именованный аргумент.

Изменено в версии 3.14: Добавлена поддержка Placeholder в позиционных аргументах.

class functools.partialmethod(func, /, *args, **keywords)

Возвращает новый дескриптор partialmethod, который ведёт себя как partial, за исключением того, что он предназначен для использования в качестве определения метода, а не для прямого вызова.

func должен быть дескриптором или вызываемым объектом (объекты, являющиеся и тем и другим, как обычные функции, обрабатываются как дескрипторы).

Когда func является дескриптором (например, обычная функция Python, classmethod(), staticmethod(), abstractmethod() или другой экземпляр partialmethod), вызовы __get__ делегируются базовому дескриптору, а в качестве результата возвращается подходящий частичный объект.

Когда 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, /[, initial])

Применяет function от двух аргументов кумулятивно к элементам iterable слева направо, чтобы свести итерабельный объект к одному значению. Например, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) вычисляет ((((1+2)+3)+4)+5). Левый аргумент x – это накопленное значение, а правый аргумент y – это обновляемое значение из iterable. Если указан необязательный initial, он помещается перед элементами итерабельного объекта в вычислениях и служит значением по умолчанию, когда итерабельный объект пуст. Если initial не задан, а iterable содержит только один элемент, возвращается первый элемент.

Примерно эквивалентно:

initial_missing = sentinel('initial_missing')

def reduce(function, iterable, /, initial=initial_missing):
    it = iter(iterable)
    if initial is initial_missing:
        value = next(it)
    else:
        value = initial
    for element in it:
        value = function(value, element)
    return value

См. itertools.accumulate() для итератора, который возвращает все промежуточные значения.

Изменено в версии 3.14: initial теперь поддерживается как именованный аргумент.

@functools.singledispatch

Преобразует функцию в однодиспетчерскую обобщённую функцию.

Чтобы определить обобщённую функцию, декорируйте её с помощью декоратора @singledispatch. При определении функции с помощью @singledispatch учтите, что диспетчеризация происходит по типу первого аргумента:

>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
...     if verbose:
...         print("Let me just say,", end=" ")
...     print(arg)

Чтобы добавить перегруженные реализации функции, используйте атрибут register() обобщённой функции, который можно использовать как декоратор. Для функций с аннотациями типов декоратор автоматически определяет тип первого аргумента:

>>> @fun.register
... def _(arg: int, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> @fun.register
... def _(arg: list, verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

typing.Union также можно использовать:

>>> @fun.register
... def _(arg: int | float, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> from typing import Union
>>> @fun.register
... def _(arg: Union[list, set], 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)
...

Для кода, который выполняет диспетчеризацию по типу коллекции (например, list), но хочет аннотировать типы элементов коллекции (например, list[int]), тип для диспетчеризации следует явно передать самому декоратору, а аннотация типа указывается в определении функции:

>>> @fun.register(list)
... def _(arg: list[int], verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

Примечание

Во время выполнения функция будет выполнять диспетчеризацию по экземпляру списка независимо от типа элементов внутри списка, то есть [1,2,3] будет диспетчеризоваться так же, как ["foo", "bar", "baz"]. Аннотация, приведённая в этом примере, предназначена только для статических проверок типов и не влияет на выполнение.

Чтобы можно было регистрировать лямбды и уже существующие функции, атрибут 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

Если для конкретного типа нет зарегистрированной реализации, используется порядок разрешения методов (MRO) этого типа для поиска более общей реализации. Исходная функция, декорированная @singledispatch, зарегистрирована для базового типа object, то есть она используется, если не найдено более подходящей реализации.

Если реализация зарегистрирована для абстрактного базового класса, виртуальные подклассы этого базового класса будут диспетчеризоваться к этой реализации:

>>> from collections.abc import Mapping
>>> @fun.register
... def _(arg: Mapping, verbose=False):
...     if verbose:
...         print("Keys & Values")
...     for key, value in arg.items():
...         print(key, "=>", value)
...
>>> fun({"a": "b"})
a => b

Чтобы проверить, какую реализацию выберет обобщённая функция для заданного типа, используйте атрибут dispatch():

>>> fun.dispatch(float)
<function fun_num at 0x1035a2840>
>>> fun.dispatch(dict)    # примечание: реализация по умолчанию
<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() теперь поддерживает использование аннотаций типов.

Изменено в версии 3.11: Атрибут register() теперь поддерживает typing.Union в качестве аннотации типа.

class functools.singledispatchmethod(func)

Преобразует метод в однодиспетчерскую обобщённую функцию.

Чтобы определить обобщённый метод, декорируйте его с помощью декоратора @singledispatchmethod. При определении метода с помощью @singledispatchmethod учтите, что диспетчеризация происходит по типу первого аргумента, не являющегося self или cls:

class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg

@singledispatchmethod поддерживает вложение с другими декораторами, такими как @classmethod. Обратите внимание, что для поддержки dispatcher.register singledispatchmethod должен быть самым внешним декоратором. Вот класс Negator с методами neg, привязанными к классу, а не к экземпляру класса:

class Negator:
    @singledispatchmethod
    @classmethod
    def neg(cls, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    @classmethod
    def _(cls, arg: int):
        return -arg

    @neg.register
    @classmethod
    def _(cls, arg: bool):
        return not arg

Тот же шаблон можно использовать для других подобных декораторов: @staticmethod, @~abc.abstractmethod и других.

Добавлено в версии 3.8.

Изменено в версии 3.15: Добавлена поддержка вызываемых объектов, не являющихся дескрипторами.

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

Обновляет функцию-обёртку так, чтобы она выглядела как обёрнутую функцию. Необязательные аргументы – кортежи, определяющие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки, а какие атрибуты функции-обёртки обновляются соответствующими атрибутами исходной функции. Значения по умолчанию для этих аргументов – константы уровня модуля WRAPPER_ASSIGNMENTS (которая присваивает функции-обёртке атрибуты __module__, __name__, __qualname__, __annotations__, __type_params__ и __doc__ – строку документации) и WRAPPER_UPDATES (которая обновляет __dict__ функции-обёртки, т.е. словарь экземпляра).

Чтобы обеспечить доступ к исходной функции для интроспекции и других целей (например, обхода кэширующего декоратора такого как lru_cache()), эта функция автоматически добавляет атрибут __wrapped__ к обёртке, который ссылается на обёрнутую функцию.

Основное назначение этой функции – использование в decorator-функциях, которые оборачивают декорированную функцию и возвращают обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно не очень полезно.

update_wrapper() можно использовать с вызываемыми объектами, отличными от функций. Любые атрибуты, указанные в assigned или updated, отсутствующие в оборачиваемом объекте, игнорируются (т.е. эта функция не будет пытаться установить их в функции-обёртке). AttributeError по-прежнему возбуждается, если сама функция-обёртка не имеет каких-либо атрибутов, перечисленных в updated.

Изменено в версии 3.2: Атрибут __wrapped__ теперь добавляется автоматически. Атрибут __annotations__ теперь копируется по умолчанию. Отсутствующие атрибуты больше не вызывают AttributeError.

Изменено в версии 3.4: Атрибут __wrapped__ теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут __wrapped__. (см. bpo-17482)

Изменено в версии 3.12: Атрибут __type_params__ теперь копируется по умолчанию.

@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():
...     """докстринг"""
...     print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'

Без использования этой фабрики декораторов имя примера функции было бы 'wrapper', а строка документации исходной example() была бы потеряна.

partial Объектыpartial Objects

Объекты partial – это вызываемые объекты, создаваемые partial(). Они имеют три атрибута только для чтения:

partial.func

Вызываемый объект или функция. Вызовы объекта partial будут перенаправлены в func с новыми аргументами и ключевыми словами.

partial.args

Крайние левые позиционные аргументы, которые будут добавлены перед позиционными аргументами, переданными при вызове объекта partial.

partial.keywords

Ключевые аргументы, которые будут предоставлены при вызове объекта partial.

Объекты partial похожи на объекты функций тем, что они вызываемы, поддерживают слабые ссылки и могут иметь атрибуты. Есть несколько важных отличий. Например, атрибуты __name__ и __doc__ не создаются автоматически.