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

functools.md

472 строк · 33.9 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.8/library/functools.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`functools`](https://python-all.ru/3.8/library/functools.html#module-functools) – Функции высшего порядка и операции над вызываемыми объектами89**Исходный код:** [Lib/functools.py](https://python-all.ru/src/3.8/Lib/functools.py)1011---1213Модуль [`functools`](https://python-all.ru/3.8/library/functools.html#module-functools) предназначен для функций высшего порядка: функций, которые действуют на или возвращают другие функции. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.1415Модуль [`functools`](https://python-all.ru/3.8/library/functools.html#module-functools) определяет следующие функции:1617#### `@functools.cached_property(func)`1819Преобразует метод класса в свойство, значение которого вычисляется один раз и затем кэшируется как обычный атрибут на всё время существования экземпляра. Аналогично [`property()`](https://python-all.ru/3.8/library/functions.html#property), но с добавлением кэширования. Полезно для дорогостоящих вычисляемых свойств экземпляров, которые в остальном практически неизменны.2021Пример:2223```python24class DataSet:25    def __init__(self, sequence_of_numbers):26        self._data = sequence_of_numbers2728    @cached_property29    def stdev(self):30        return statistics.stdev(self._data)3132    @cached_property33    def variance(self):34        return statistics.variance(self._data)35```3637Новое в версии 3.8.3839> **Примечание**40>41> Этот декоратор требует, чтобы атрибут `__dict__` каждого экземпляра был изменяемым отображением. Это означает, что он не будет работать с некоторыми типами, например, метаклассами (поскольку атрибуты `__dict__` экземпляров типа являются прокси только для чтения для пространства имён класса), а также с теми, которые указывают `__slots__` без включения `__dict__` в качестве одного из определённых слотов (поскольку такие классы вообще не предоставляют атрибут `__dict__`).4243#### `functools.cmp_to_key(func)`4445Преобразует старую функцию сравнения в [функцию ключа](https://python-all.ru/3.8/glossary.html#term-key-function). Используется с инструментами, которые принимают функции ключа (такими как [`sorted()`](https://python-all.ru/3.8/library/functions.html#sorted), [`min()`](https://python-all.ru/3.8/library/functions.html#min), [`max()`](https://python-all.ru/3.8/library/functions.html#max), [`heapq.nlargest()`](https://python-all.ru/3.8/library/heapq.html#heapq.nlargest), [`heapq.nsmallest()`](https://python-all.ru/3.8/library/heapq.html#heapq.nsmallest), [`itertools.groupby()`](https://python-all.ru/3.8/library/itertools.html#itertools.groupby)). Эта функция в первую очередь используется как инструмент перехода для программ, переносимых с Python 2, который поддерживал использование функций сравнения.4647Функция сравнения – это любой вызываемый объект, который принимает два аргумента, сравнивает их и возвращает отрицательное число, если первый меньше второго, ноль при равенстве или положительное число, если первый больше второго. Функция ключа – это вызываемый объект, который принимает один аргумент и возвращает другое значение, используемое в качестве ключа сортировки.4849Пример:5051```python52sorted(iterable, key=cmp_to_key(locale.strcoll))  # порядок сортировки с учётом локали53```5455Примеры сортировки и краткое руководство см. в [Sorting HOW TO](https://python-all.ru/3.8/howto/sorting.html#sortinghowto).5657Новое в версии 3.2.5859#### `@functools.lru_cache(user_function)`6061#### `@functools.lru_cache(maxsize=128, typed=False)`6263Декоратор для обёртывания функции запоминающим вызываемым объектом, который сохраняет до *maxsize* последних вызовов. Это может сэкономить время, когда дорогостоящая или связанная с вводом-выводом функция периодически вызывается с одними и теми же аргументами.6465Поскольку для кеширования результатов используется словарь, позиционные и именованные аргументы функции должны быть хешируемыми.6667Различные наборы аргументов могут считаться разными вызовами с отдельными записями в кеше. Например, *f(a=1, b=2)* и *f(b=2, a=1)* различаются порядком именованных аргументов и могут иметь две отдельные записи в кеше.6869Если указан *user\_function*, он должен быть вызываемым. Это позволяет применить декоратор *lru\_cache* непосредственно к пользовательской функции, оставляя *maxsize* со значением по умолчанию 128:7071```python72@lru_cache73def count_vowels(sentence):74    sentence = sentence.casefold()75    return sum(sentence.count(vowel) for vowel in 'aeiou')76```7778Если *maxsize* установлен в `None`, механизм LRU отключается и кэш может расти без ограничений.7980Если для *typed* установлено значение true, аргументы функции разных типов будут кешироваться отдельно. Например, `f(3)` и `f(3.0)` будут считаться разными вызовами с разными результатами.8182Чтобы оценить эффективность кеша и настроить параметр *maxsize*, обёрнутая функция дополняется функцией `cache_info()`, которая возвращает [именованный кортеж](https://python-all.ru/3.8/glossary.html#term-named-tuple), показывающий *hits* (попадания), *misses* (промахи), *maxsize* и *currsize*. В многопоточной среде значения попаданий и промахов являются приблизительными.8384Декоратор также предоставляет функцию `cache_clear()` для очистки или инвалидации кэша.8586Исходная базовая функция доступна через атрибут `__wrapped__`. Это полезно для интроспекции, обхода кэша или повторной обёртки функции другим кэшем.8788Кэш [LRU (least recently used – вытеснение давно неиспользуемых)](https://python-all.ru/3.8/library/functools.html) работает наилучшим образом, когда самые последние вызовы являются лучшими предикторами будущих вызовов (например, самые популярные статьи на новостном сервере имеют тенденцию меняться каждый день). Ограничение размера кэша гарантирует, что он не будет бесконечно расти в долго работающих процессах, таких как веб-серверы.8990В целом, LRU-кеш следует использовать только тогда, когда нужно повторно использовать ранее вычисленные значения. Соответственно, нет смысла кешировать функции с побочными эффектами, функции, которым нужно создавать различные изменяемые объекты при каждом вызове, или нечистые функции, такие как time() или random().9192Пример LRU-кэша для статического веб-контента:9394```python95@lru_cache(maxsize=32)96def get_pep(num):97    'Retrieve text of a Python Enhancement Proposal'98    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num99    try:100        with urllib.request.urlopen(resource) as s:101            return s.read()102    except urllib.error.HTTPError:103        return 'Not Found'104105>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:106...     pep = get_pep(n)107...     print(n, len(pep))108109>>> get_pep.cache_info()110CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)111```112113Пример эффективного вычисления [чисел Фибоначчи](https://python-all.ru/3.8/library/functools.html) с использованием кэша для реализации техники [динамического программирования](https://python-all.ru/3.8/library/functools.html):114115```python116@lru_cache(maxsize=None)117def fib(n):118    if n < 2:119        return n120    return fib(n-1) + fib(n-2)121122>>> [fib(n) for n in range(16)]123[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]124125>>> fib.cache_info()126CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)127```128129Новое в версии 3.2.130131Изменено в версии 3.3: Добавлена опция *typed*.132133Изменено в версии 3.8: Добавлена опция *user\_function*.134135#### `@functools.total_ordering`136137Для класса, определяющего один или несколько методов расширенного сравнения, этот декоратор класса предоставляет остальные. Это упрощает задачу определения всех возможных операций расширенного сравнения:138139Класс должен определять один из [`__lt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__gt__) или [`__ge__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__ge__). Кроме того, класс должен предоставлять метод [`__eq__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__eq__).140141Например:142143```python144@total_ordering145class Student:146    def _is_valid_operand(self, other):147        return (hasattr(other, "lastname") and148                hasattr(other, "firstname"))149    def __eq__(self, other):150        if not self._is_valid_operand(other):151            return NotImplemented152        return ((self.lastname.lower(), self.firstname.lower()) ==153                (other.lastname.lower(), other.firstname.lower()))154    def __lt__(self, other):155        if not self._is_valid_operand(other):156            return NotImplemented157        return ((self.lastname.lower(), self.firstname.lower()) <158                (other.lastname.lower(), other.firstname.lower()))159```160161> **Примечание**162>163> Хотя этот декоратор упрощает создание хорошо ведущих себя полностью упорядоченных типов, он *действительно* приводит к более медленному выполнению и более сложным трассировкам стека для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для конкретного приложения, реализация всех шести методов расширенного сравнения вместо этого, вероятно, даст лёгкий прирост скорости.164165Новое в версии 3.2.166167Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из нижележащей функции сравнения для нераспознанных типов.168169#### `functools.partial(func, /, *args, **keywords)`170171Возвращает новый [объект partial](https://python-all.ru/3.8/library/functools.html#partial-objects), который при вызове будет вести себя как *func*, вызванная с позиционными аргументами *args* и именованными аргументами *keywords*. Если при вызове передаётся больше аргументов, они добавляются к *args*. Если передаются дополнительные именованные аргументы, они расширяют и переопределяют *keywords*. Примерно эквивалентно следующему:172173```python174def partial(func, /, *args, **keywords):175    def newfunc(*fargs, **fkeywords):176        newkeywords = {**keywords, **fkeywords}177        return func(*args, *fargs, **newkeywords)178    newfunc.func = func179    newfunc.args = args180    newfunc.keywords = keywords181    return newfunc182```183184[`partial()`](https://python-all.ru/3.8/library/functools.html#functools.partial) используется для частичного применения функции, которое «замораживает» некоторую часть аргументов и/или ключевых слов функции, создавая новый объект с упрощённой сигнатурой. Например, [`partial()`](https://python-all.ru/3.8/library/functools.html#functools.partial) можно использовать для создания вызываемого объекта, который ведёт себя как функция [`int()`](https://python-all.ru/3.8/library/functions.html#int), где аргумент *base* по умолчанию равен двум:185186```python187>>> from functools import partial188>>> basetwo = partial(int, base=2)189>>> basetwo.__doc__ = 'Convert base 2 string to an int.'190>>> basetwo('10010')19118192```193194#### `class functools.partialmethod(func, /, *args, **keywords)`195196Возвращает новый дескриптор [`partialmethod`](https://python-all.ru/3.8/library/functools.html#functools.partialmethod), который ведёт себя как [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial), за исключением того, что он предназначен для использования в качестве определения метода, а не для прямого вызова.197198*func* должен быть [дескриптором](https://python-all.ru/3.8/glossary.html#term-descriptor) или вызываемым объектом (объекты, являющиеся и тем и другим, как обычные функции, обрабатываются как дескрипторы).199200Когда *func* является дескриптором (например, обычная функция Python, [`classmethod()`](https://python-all.ru/3.8/library/functions.html#classmethod), [`staticmethod()`](https://python-all.ru/3.8/library/functions.html#staticmethod), `abstractmethod()` или другой экземпляр [`partialmethod`](https://python-all.ru/3.8/library/functools.html#functools.partialmethod)), вызовы `__get__` делегируются базовому дескриптору, а в качестве результата возвращается подходящий [частичный объект](https://python-all.ru/3.8/library/functools.html#partial-objects).201202Когда *func* – это вызываемый объект, не являющийся дескриптором, динамически создаётся соответствующий связанный метод. Он ведёт себя как обычная функция Python при использовании в качестве метода: аргумент *self* будет вставлен как первый позиционный аргумент, даже до *args* и *keywords*, переданных конструктору [`partialmethod`](https://python-all.ru/3.8/library/functools.html#functools.partialmethod).203204Пример:205206```python207>>> class Cell(object):208...     def __init__(self):209...         self._alive = False210...     @property211...     def alive(self):212...         return self._alive213...     def set_state(self, state):214...         self._alive = bool(state)215...     set_alive = partialmethod(set_state, True)216...     set_dead = partialmethod(set_state, False)217...218>>> c = Cell()219>>> c.alive220False221>>> c.set_alive()222>>> c.alive223True224```225226Новое в версии 3.4.227228#### `functools.reduce(function, iterable[, initializer])`229230Применяет *функцию* с двумя аргументами последовательно к элементам *итерируемого объекта* слева направо, чтобы свести итерируемый объект к одному значению. Например, `reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` вычисляет `((((1+2)+3)+4)+5)`. Левый аргумент *x* – накопленное значение, а правый аргумент *y* – очередное значение из *итерируемого объекта*. Если указан необязательный *инициализатор*, он помещается перед элементами итерируемого объекта в вычислениях и служит значением по умолчанию, когда итерируемый объект пуст. Если *инициализатор* не указан, а *итерируемый объект* содержит только один элемент, возвращается первый элемент.231232Примерно эквивалентно:233234```python235def reduce(function, iterable, initializer=None):236    it = iter(iterable)237    if initializer is None:238        value = next(it)239    else:240        value = initializer241    for element in it:242        value = function(value, element)243    return value244```245246См. [`itertools.accumulate()`](https://python-all.ru/3.8/library/itertools.html#itertools.accumulate) для итератора, который возвращает все промежуточные значения.247248#### `@functools.singledispatch`249250Преобразует функцию в [однодиспетчерскую](https://python-all.ru/3.8/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.8/glossary.html#term-generic-function).251252Чтобы определить обобщённую функцию, декорируйте её декоратором `@singledispatch`. Обратите внимание, что диспетчеризация осуществляется по типу первого аргумента, создавайте свою функцию соответственно:253254```python255>>> from functools import singledispatch256>>> @singledispatch257... def fun(arg, verbose=False):258...     if verbose:259...         print("Let me just say,", end=" ")260...     print(arg)261```262263Чтобы добавить перегруженные реализации к функции, используйте атрибут `register()` обобщённой функции. Это декоратор. Для функций, аннотированных типами, декоратор автоматически выводит тип первого аргумента:264265```python266>>> @fun.register267... def _(arg: int, verbose=False):268...     if verbose:269...         print("Strength in numbers, eh?", end=" ")270...     print(arg)271...272>>> @fun.register273... def _(arg: list, verbose=False):274...     if verbose:275...         print("Enumerate this:")276...     for i, elem in enumerate(arg):277...         print(i, elem)278```279280Для кода, не использующего аннотации типов, подходящий тип можно передать явно самому декоратору:281282```python283>>> @fun.register(complex)284... def _(arg, verbose=False):285...     if verbose:286...         print("Better than complicated.", end=" ")287...     print(arg.real, arg.imag)288...289```290291Чтобы включить регистрацию лямбда-функций и уже существующих функций, атрибут `register()` можно использовать в функциональной форме:292293```python294>>> def nothing(arg, verbose=False):295...     print("Nothing.")296...297>>> fun.register(type(None), nothing)298```299300Атрибут `register()` возвращает недекорированную функцию, что позволяет накладывать декораторы, выполнять сериализацию и создавать модульные тесты для каждого варианта независимо:301302```python303>>> @fun.register(float)304... @fun.register(Decimal)305... def fun_num(arg, verbose=False):306...     if verbose:307...         print("Half of your number:", end=" ")308...     print(arg / 2)309...310>>> fun_num is fun311False312```313314При вызове обобщённая функция выполняет диспетчеризацию по типу первого аргумента:315316```python317>>> fun("Hello, world.")318Hello, world.319>>> fun("test.", verbose=True)320Let me just say, test.321>>> fun(42, verbose=True)322Strength in numbers, eh? 42323>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)324Enumerate this:3250 spam3261 spam3272 eggs3283 spam329>>> fun(None)330Nothing.331>>> fun(1.23)3320.615333```334335Если для конкретного типа нет зарегистрированной реализации, используется порядок разрешения методов (MRO) этого типа для поиска более общей реализации. Исходная функция, декорированная `@singledispatch`, зарегистрирована для базового типа `object`, то есть она используется, если не найдено более подходящей реализации.336337Чтобы проверить, какую реализацию выберет обобщённая функция для заданного типа, используйте атрибут `dispatch()`:338339```python340>>> fun.dispatch(float)341<function fun_num at 0x1035a2840>342>>> fun.dispatch(dict)    # примечание: реализация по умолчанию343<function fun at 0x103fe0000>344```345346Чтобы получить доступ ко всем зарегистрированным реализациям, используйте атрибут `registry` только для чтения:347348```python349>>> fun.registry.keys()350dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,351          <class 'decimal.Decimal'>, <class 'list'>,352          <class 'float'>])353>>> fun.registry[float]354<function fun_num at 0x1035a2840>355>>> fun.registry[object]356<function fun at 0x103fe0000>357```358359Новое в версии 3.4.360361Изменено в версии 3.7: Атрибут `register()` теперь поддерживает использование аннотаций типов.362363#### `class functools.singledispatchmethod(func)`364365Преобразует метод в [однодиспетчерскую](https://python-all.ru/3.8/glossary.html#term-single-dispatch) [обобщённую функцию](https://python-all.ru/3.8/glossary.html#term-generic-function).366367Чтобы определить обобщённый метод, декорируйте его декоратором `@singledispatchmethod`. Обратите внимание, что диспетчеризация осуществляется по типу первого аргумента, не являющегося self или cls. Создавайте свою функцию соответственно:368369```python370class Negator:371    @singledispatchmethod372    def neg(self, arg):373        raise NotImplementedError("Cannot negate a")374375    @neg.register376    def _(self, arg: int):377        return -arg378379    @neg.register380    def _(self, arg: bool):381        return not arg382```383384`@singledispatchmethod` поддерживает вложенность с другими декораторами, такими как `@classmethod`. Обратите внимание, чтобы обеспечить `dispatcher.register`, `singledispatchmethod` должен быть *самым внешним* декоратором. Вот класс `Negator` с методами `neg`, привязанными к классу:385386```python387class Negator:388    @singledispatchmethod389    @classmethod390    def neg(cls, arg):391        raise NotImplementedError("Cannot negate a")392393    @neg.register394    @classmethod395    def _(cls, arg: int):396        return -arg397398    @neg.register399    @classmethod400    def _(cls, arg: bool):401        return not arg402```403404Тот же шаблон можно использовать для других похожих декораторов: `staticmethod`, `abstractmethod` и других.405406Новое в версии 3.8.407408#### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`409410Обновляет функцию-*обёртку* так, чтобы она походила на *обёрнутую* функцию. Необязательные аргументы – это кортежи, определяющие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки, а какие атрибуты функции-обёртки обновляются соответствующими атрибутами из исходной функции. Значения по умолчанию для этих аргументов – константы уровня модуля `WRAPPER_ASSIGNMENTS` (которая присваивает функции-обёртке атрибуты `__module__`, `__name__`, `__qualname__`, `__annotations__` и `__doc__`, строку документации) и `WRAPPER_UPDATES` (которая обновляет `__dict__` функции-обёртки, т.е. словарь экземпляра).411412Чтобы обеспечить доступ к исходной функции для интроспекции и других целей (например, обхода кэширующего декоратора такого как [`lru_cache()`](https://python-all.ru/3.8/library/functools.html#functools.lru_cache)), эта функция автоматически добавляет атрибут `__wrapped__` к обёртке, который ссылается на обёрнутую функцию.413414Основное назначение этой функции – использование в [decorator](https://python-all.ru/3.8/glossary.html#term-decorator)-функциях, которые оборачивают декорированную функцию и возвращают обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно не очень полезно.415416[`update_wrapper()`](https://python-all.ru/3.8/library/functools.html#functools.update_wrapper) можно использовать с вызываемыми объектами, отличными от функций. Любые атрибуты, указанные в *assigned* или *updated*, отсутствующие в оборачиваемом объекте, игнорируются (т.е. эта функция не будет пытаться установить их в функции-обёртке). [`AttributeError`](https://python-all.ru/3.8/library/exceptions.html#AttributeError) по-прежнему возбуждается, если сама функция-обёртка не имеет каких-либо атрибутов, перечисленных в *updated*.417418Новое в версии 3.2: Автоматическое добавление атрибута `__wrapped__`.419420Новое в версии 3.2: Копирование атрибута `__annotations__` по умолчанию.421422Изменено в версии 3.2: Отсутствующие атрибуты больше не вызывают исключение [`AttributeError`](https://python-all.ru/3.8/library/exceptions.html#AttributeError).423424Изменено в версии 3.4: Атрибут `__wrapped__` теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут `__wrapped__`. (см. [bpo-17482](https://python-all.ru/3.8/library/functools.html))425426#### `@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`427428Это удобная функция для вызова [`update_wrapper()`](https://python-all.ru/3.8/library/functools.html#functools.update_wrapper) в качестве декоратора функции при определении функции-обёртки. Она эквивалентна `partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`. Например:429430```python431>>> from functools import wraps432>>> def my_decorator(f):433...     @wraps(f)434...     def wrapper(*args, **kwds):435...         print('Calling decorated function')436...         return f(*args, **kwds)437...     return wrapper438...439>>> @my_decorator440... def example():441...     """докстринг"""442...     print('Called example function')443...444>>> example()445Calling decorated function446Called example function447>>> example.__name__448'example'449>>> example.__doc__450'Docstring'451```452453Без использования этой фабрики декораторов имя примера функции было бы `'wrapper'`, а строка документации исходной `example()` была бы потеряна.454455## [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial) Объекты456457Объекты [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial) – это вызываемые объекты, создаваемые [`partial()`](https://python-all.ru/3.8/library/functools.html#functools.partial). Они имеют три атрибута только для чтения:458459#### `partial.func`460461Вызываемый объект или функция. Вызовы объекта [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial) будут перенаправлены в [`func`](https://python-all.ru/3.8/library/functools.html#functools.partial.func) с новыми аргументами и ключевыми словами.462463#### `partial.args`464465Крайние левые позиционные аргументы, которые будут добавлены перед позиционными аргументами, переданными при вызове объекта [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial).466467#### `partial.keywords`468469Ключевые аргументы, которые будут предоставлены при вызове объекта [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial).470471Объекты [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial) похожи на объекты `function` тем, что они вызываемы, могут быть слабыми ссылками и могут иметь атрибуты. Есть несколько важных отличий. Например, атрибуты [`__name__`](https://python-all.ru/3.8/library/stdtypes.html#definition.__name__) и `__doc__` не создаются автоматически. Кроме того, объекты [`partial`](https://python-all.ru/3.8/library/functools.html#functools.partial), определённые в классах, ведут себя как статические методы и не превращаются в связанные методы при поиске атрибута экземпляра.472