functools — Функции высшего порядка и операции над вызываемыми объектами
Исходный код: 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) # no previously cached result, makes 11 recursive calls 3628800 >>> factorial(5) # no new calls, just returns the cached result 120 >>> factorial(12) # two new recursive calls, factorial(10) is cached 479001600Кэш потокобезопасен, поэтому обёрнутую функцию можно использовать в нескольких потоках. Это означает, что базовая структура данных останется согласованной при одновременных обновлениях.
Обёрнутая функция может быть вызвана более одного раза, если другой поток выполнит дополнительный вызов до завершения и кэширования первоначального вызова.
Добавлено в версии 3.9.
-
@functools.cached_property(func) -
Преобразует метод класса в свойство, значение которого вычисляется один раз, а затем кэшируется как обычный атрибут на всё время существования экземпляра. Подобно
@property, но с кэшированием. Полезно для ресурсоёмких вычисляемых свойств экземпляров, которые в остальном фактически неизменяемы.Пример:
class DataSet: def __init__(self, sequence_of_numbers): self._data = tuple(sequence_of_numbers) @cached_property def stdev(self): return statistics.stdev(self._data)Механизм работы
@cached_propertyнесколько отличается от механизма@property. Обычное свойство блокирует запись атрибутов, если не определён сеттер. В отличие от него, cached_property допускает запись.Декоратор cached_property срабатывает только при поиске атрибута и только если атрибут с таким именем ещё не существует. При срабатывании cached_property записывает значение в атрибут с тем же именем. При последующих чтениях и записях атрибута приоритет имеет сам атрибут, а не метод cached_property; он работает как обычный атрибут.
Кэшированное значение можно очистить, удалив атрибут. Это позволит методу cached_property сработать снова.
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)) # locale-aware sort order
Примеры сортировки и краткое руководство по сортировке см. в разделе Приёмы сортировки.
Добавлено в версии 3.2.
-
@functools.lru_cache(user_function) - @functools.lru_cache(maxsize=128, typed=False)
-
Декоратор, оборачивающий функцию в вызываемый объект с мемоизацией, который сохраняет до maxsize последних вызовов. Он позволяет экономить время, если ресурсоёмкая функция или функция с ограничениями ввода-вывода периодически вызывается с одними и теми же аргументами.
Кэш потокобезопасен, поэтому обёрнутую функцию можно использовать в нескольких потоках. Это означает, что базовая структура данных останется согласованной при одновременных обновлениях.
Обёрнутая функция может быть вызвана более одного раза, если другой поток выполнит дополнительный вызов до завершения и кэширования первоначального вызова.
Поскольку для кэширования результатов используется словарь, позиционные и именованные аргументы функции должны быть хешируемыми.
Различные наборы аргументов могут считаться разными вызовами с отдельными записями в кэше. Например,
f(a=1, b=2)иf(b=2, a=1)отличаются порядком именованных аргументов и могут иметь две отдельные записи в кэше.Если задан параметр user_function, его значение должно быть вызываемым объектом. Это позволяет применять декоратор lru_cache непосредственно к пользовательской функции, оставляя для maxsize значение по умолчанию, равное 128:
@lru_cache def count_vowels(sentence): return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')Если для maxsize задано значение
None, функция LRU отключается, и кэш может расти без ограничений.Если для typed задано значение true, аргументы функции разных типов будут кэшироваться отдельно. Если для typed задано значение false, реализация обычно считает их эквивалентными вызовами и кэширует только один результат. (Некоторые типы, например str и int, могут кэшироваться отдельно, даже если для typed задано значение false.)
Обратите внимание: различия типов учитываются только для непосредственных аргументов функции, но не для их содержимого. Скалярные аргументы
Decimal(42)иFraction(42)рассматриваются как разные вызовы с разными результатами. В отличие от них, аргументы-кортежи('answer', Decimal(42))и('answer', Fraction(42))считаются эквивалентными.К обёрнутой функции добавляется функция
cache_parameters(), возвращающая новыйdictсо значениями maxsize и typed. Она предназначена только для получения информации. Изменение этих значений ни на что не влияет.Чтобы помочь оценить эффективность кэша и настроить параметр maxsize, к обёрнутой функции добавляется функция
cache_info(), возвращающая именованный кортеж со значениями hits, misses, maxsize и currsize.Декоратор также предоставляет функцию
cache_clear()для очистки или сброса кэша.Доступ к исходной функции можно получить через атрибут
__wrapped__. Это полезно для интроспекции, обхода кэша или повторного оборачивания функции с другим кэшем.Кэш хранит ссылки на аргументы и возвращаемые значения, пока они не устареют в кэше или пока кэш не будет очищен.
Если кэшируется метод, аргумент экземпляра
selfвключается в кэш. См. раздел Как кэшировать вызовы методов?Кэш LRU (Least Recently Used — вытеснение давно не использовавшихся элементов) лучше всего работает, когда последние вызовы лучше всего предсказывают будущие (например, самые популярные статьи на новостном сервере обычно меняются ежедневно). Ограничение размера кэша не позволяет ему бесконтрольно расти в долго работающих процессах, таких как веб-серверы.
В общем случае кэш 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__делегируются нижележащему дескриптору, а результатом становится соответствующий объект partial.Если 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 = object() 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Если для определённого типа нет зарегистрированной реализации, используется порядок разрешения методов этого типа для поиска более общей реализации. Исходная функция, декорированная с помощью
@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) # note: default implementation <function fun at 0x103fe0000>
Чтобы получить доступ ко всем зарегистрированным реализациям, используйте доступный только для чтения атрибут
registry:>>> fun.registry.keys() dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>, <class 'decimal.Decimal'>, <class 'list'>, <class 'float'>]) >>> fun.registry[float] <function fun_num at 0x1035a2840> >>> fun.registry[object] <function fun at 0x103fe0000>Добавлено в версии 3.4.
Изменено в версии 3.7: Атрибут
register()теперь поддерживает использование аннотаций типов.Изменено в версии 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.
-
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__к обёртке, который ссылается на оборачиваемую функцию.Основное назначение этой функции — использование в функциях-декораторах, которые оборачивают декорируемую функцию и возвращают обёртку. Если функция-обёртка не обновлена, метаданные возвращённой функции будут отражать определение обёртки, а не исходной функции, что обычно мало полезно.
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(): ... """Docstring""" ... print('Called example function') ... >>> example() Calling decorated function Called example function >>> example.__name__ 'example' >>> example.__doc__ 'Docstring'Без использования этой фабрики декораторов имя примерной функции было бы
'wrapper', а строка документации исходной функцииexample()была бы потеряна.
Объекты partial
Объекты partial — это вызываемые объекты, созданные с помощью partial(). У них есть три атрибута, доступных только для чтения:
-
partial.func -
Вызываемый объект или функция. Вызовы объекта
partialбудут перенаправляться кfuncс новыми аргументами и именованными аргументами.
-
partial.args -
Самые левые позиционные аргументы, которые будут добавлены в начало позиционных аргументов, переданных при вызове объекта
partial.
-
partial.keywords -
Именованные аргументы, которые будут переданы при вызове объекта
partial.
Объекты partial подобны объектам функций: они являются вызываемыми объектами, поддерживают слабые ссылки и могут иметь атрибуты. Однако есть несколько важных различий. Например, атрибуты __name__ и __doc__ не создаются автоматически.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/functools.html