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

functools.md

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

1> **Источник:** https://python-all.ru/3.4/library/functools.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 10.2. [`functools`](https://python-all.ru/3.4/library/functools.html#module-functools) – Функции высшего порядка и операции с вызываемыми объектами89**Исходный код:** [Lib/functools.py](https://python-all.ru/src/3.4/Lib/functools.py)1011---1213Модуль [`functools`](https://python-all.ru/3.4/library/functools.html#module-functools) предназначен для функций высшего порядка: функций, которые действуют на другие функции или возвращают их. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.1415Модуль [`functools`](https://python-all.ru/3.4/library/functools.html#module-functools) определяет следующие функции:1617#### `functools.cmp_to_key(func)`1819Преобразует функцию сравнения старого образца в [*ключевую функцию*](https://python-all.ru/3.4/glossary.html#term-key-function). Используется с инструментами, которые принимают ключевые функции (например, [`sorted()`](https://python-all.ru/3.4/library/functions.html#sorted), [`min()`](https://python-all.ru/3.4/library/functions.html#min), [`max()`](https://python-all.ru/3.4/library/functions.html#max), [`heapq.nlargest()`](https://python-all.ru/3.4/library/heapq.html#heapq.nlargest), [`heapq.nsmallest()`](https://python-all.ru/3.4/library/heapq.html#heapq.nsmallest), [`itertools.groupby()`](https://python-all.ru/3.4/library/itertools.html#itertools.groupby)). Эта функция в первую очередь используется как переходный инструмент для программ, переносимых из Python 2, который поддерживал использование функций сравнения.2021Функция сравнения – это любой вызываемый объект, который принимает два аргумента, сравнивает их и возвращает отрицательное число, если первый меньше второго, ноль при равенстве или положительное число, если первый больше второго. Функция ключа – это вызываемый объект, который принимает один аргумент и возвращает другое значение, используемое в качестве ключа сортировки.2223Пример:2425```python26sorted(iterable, key=cmp_to_key(locale.strcoll))  # порядок сортировки с учётом локали27```2829Примеры сортировки и краткое руководство см. в [*Sorting HOW TO*](https://python-all.ru/3.4/howto/sorting.html#sortinghowto).3031Новое в версии 3.2.3233#### `@functools.lru_cache(maxsize=128, typed=False)`3435Декоратор для обёртывания функции запоминающим вызываемым объектом, который сохраняет до *maxsize* последних вызовов. Это может сэкономить время, когда дорогостоящая или связанная с вводом-выводом функция периодически вызывается с одними и теми же аргументами.3637Поскольку для кеширования результатов используется словарь, позиционные и именованные аргументы функции должны быть хешируемыми.3839Если *maxsize* установлен в None, механизм LRU отключается, и кэш может расти без ограничений. Механизм LRU работает лучше всего, когда *maxsize* является степенью двойки.4041Если *typed* установлен в True, аргументы функций разных типов будут кэшироваться отдельно. Например, `f(3)` и `f(3.0)` будут считаться разными вызовами с разными результатами.4243Чтобы оценить эффективность кэша и настроить параметр *maxsize*, обёрнутая функция оснащается функцией `cache_info()`, которая возвращает [*именованный кортеж*](https://python-all.ru/3.4/glossary.html#term-named-tuple), показывающий *попаданий*, *промахов*, *maxsize* и *currsize*. В многопоточной среде попадания и промахи являются приблизительными.4445Декоратор также предоставляет функцию `cache_clear()` для очистки или инвалидации кэша.4647Исходная базовая функция доступна через атрибут `__wrapped__`. Это полезно для интроспекции, обхода кэша или переоборачивания функции другим кэшем.4849Кэш [LRU (least recently used – вытеснение давно неиспользуемых)](https://python-all.ru/3.4/library/functools.html) работает наилучшим образом, когда самые последние вызовы являются лучшими предикторами будущих вызовов (например, самые популярные статьи на новостном сервере имеют тенденцию меняться каждый день). Ограничение размера кэша гарантирует, что он не будет бесконечно расти в долго работающих процессах, таких как веб-серверы.5051Пример LRU-кэша для статического веб-контента:5253```python54@lru_cache(maxsize=32)55def get_pep(num):56    'Retrieve text of a Python Enhancement Proposal'57    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num58    try:59        with urllib.request.urlopen(resource) as s:60            return s.read()61    except urllib.error.HTTPError:62        return 'Not Found'6364>>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:65...     pep = get_pep(n)66...     print(n, len(pep))6768>>> get_pep.cache_info()69CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)70```7172Пример эффективного вычисления [чисел Фибоначчи](https://python-all.ru/3.4/library/functools.html) с использованием кэша для реализации техники [динамического программирования](https://python-all.ru/3.4/library/functools.html):7374```python75@lru_cache(maxsize=None)76def fib(n):77    if n < 2:78        return n79    return fib(n-1) + fib(n-2)8081>>> [fib(n) for n in range(16)]82[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]8384>>> fib.cache_info()85CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)86```8788Новое в версии 3.2.8990Изменено в версии 3.3: Добавлена опция *typed*.9192#### `@functools.total_ordering`9394Для класса, определяющего один или несколько методов расширенного сравнения, этот декоратор класса предоставляет остальные. Это упрощает задачу определения всех возможных операций расширенного сравнения:9596Класс должен определить один из методов: [`__lt__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__gt__) или [`__ge__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__ge__). Кроме того, класс должен предоставлять метод [`__eq__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__eq__).9798Например:99100```python101@total_ordering102class Student:103    def _is_valid_operand(self, other):104        return (hasattr(other, "lastname") and105                hasattr(other, "firstname"))106    def __eq__(self, other):107        if not self._is_valid_operand(other):108            return NotImplemented109        return ((self.lastname.lower(), self.firstname.lower()) ==110                (other.lastname.lower(), other.firstname.lower()))111    def __lt__(self, other):112        if not self._is_valid_operand(other):113            return NotImplemented114        return ((self.lastname.lower(), self.firstname.lower()) <115                (other.lastname.lower(), other.firstname.lower()))116```117118> **Примечание**119>120> Хотя этот декоратор упрощает создание хорошо ведущих себя полностью упорядоченных типов, он *действительно* приводит к более медленному выполнению и более сложным трассировкам стека для производных методов сравнения. Если бенчмаркинг производительности показывает, что это является узким местом для конкретного приложения, реализация всех шести методов расширенного сравнения вместо этого, вероятно, даст лёгкий прирост скорости.121122Новое в версии 3.2.123124Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из нижележащей функции сравнения для нераспознанных типов.125126#### `functools.partial(func, *args, **keywords)`127128Возвращает новый объект [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial), который при вызове будет вести себя как *func*, вызванная с позиционными аргументами *args* и именованными аргументами *keywords*. Если в вызов передаётся больше аргументов, они добавляются к *args*. Если передаются дополнительные именованные аргументы, они расширяют и переопределяют *keywords*. Примерно эквивалентно:129130```python131def partial(func, *args, **keywords):132    def newfunc(*fargs, **fkeywords):133        newkeywords = keywords.copy()134        newkeywords.update(fkeywords)135        return func(*(args + fargs), **newkeywords)136    newfunc.func = func137    newfunc.args = args138    newfunc.keywords = keywords139    return newfunc140```141142[`partial()`](https://python-all.ru/3.4/library/functools.html#functools.partial) используется для частичного применения функции, которое «замораживает» некоторую часть аргументов и/или ключевых слов функции, создавая новый объект с упрощённой сигнатурой. Например, [`partial()`](https://python-all.ru/3.4/library/functools.html#functools.partial) можно использовать для создания вызываемого объекта, который ведёт себя как функция [`int()`](https://python-all.ru/3.4/library/functions.html#int), где аргумент *base* по умолчанию равен двум:143144```python145>>> from functools import partial146>>> basetwo = partial(int, base=2)147>>> basetwo.__doc__ = 'Convert base 2 string to an int.'148>>> basetwo('10010')14918150```151152#### `class functools.partialmethod(func, *args, **keywords)`153154Возвращает новый дескриптор [`partialmethod`](https://python-all.ru/3.4/library/functools.html#functools.partialmethod), который ведёт себя как [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial), за исключением того, что он предназначен для использования в качестве определения метода, а не для прямого вызова.155156*func* должен быть [*дескриптором*](https://python-all.ru/3.4/glossary.html#term-descriptor) или вызываемым объектом (объекты, являющиеся и тем и другим, как обычные функции, обрабатываются как дескрипторы).157158When *func* is a descriptor (such as a normal Python function, [`classmethod()`](https://python-all.ru/3.4/library/functions.html#classmethod), [`staticmethod()`](https://python-all.ru/3.4/library/functions.html#staticmethod), `abstractmethod()` or another instance of [`partialmethod`](https://python-all.ru/3.4/library/functools.html#functools.partialmethod)), calls to `__get__` are delegated to the underlying descriptor, and an appropriate [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial) object returned as the result.159160Когда *func* является вызываемым объектом, не являющимся дескриптором, динамически создается соответствующий связанный метод. Он ведёт себя как обычная функция Python при использовании в качестве метода: аргумент *self* будет вставлен в качестве первого позиционного аргумента, даже перед аргументами *args* и *keywords*, переданными конструктору [`partialmethod`](https://python-all.ru/3.4/library/functools.html#functools.partialmethod).161162Пример:163164```python165>>> class Cell(object):166...     def __init__(self):167...         self._alive = False168...     @property169...     def alive(self):170...         return self._alive171...     def set_state(self, state):172...         self._alive = bool(state)173...     set_alive = partialmethod(set_state, True)174...     set_dead = partialmethod(set_state, False)175...176>>> c = Cell()177>>> c.alive178False179>>> c.set_alive()180>>> c.alive181True182```183184Новое в версии 3.4.185186#### `functools.reduce(function, iterable[, initializer])`187188Применяет *function* с двумя аргументами последовательно к элементам *sequence* слева направо, сводя последовательность к одному значению. Например, `reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])` вычисляет `((((1+2)+3)+4)+5)`. Левый аргумент, *x*, является накопленным значением, а правый аргумент, *y*, – обновляемым значением из *sequence*. Если присутствует необязательный *initializer*, он помещается перед элементами последовательности в вычислении и служит значением по умолчанию, когда последовательность пуста. Если *initializer* не задан и *sequence* содержит только один элемент, возвращается первый элемент.189190Примерно эквивалентно:191192```python193def reduce(function, iterable, initializer=None):194    it = iter(iterable)195    if initializer is None:196        value = next(it)197    else:198        value = initializer199    for element in it:200        value = function(value, element)201    return value202```203204#### `@functools.singledispatch(default)`205206Преобразует функцию в [*одиночно-диспетчеризируемую*](https://python-all.ru/3.4/glossary.html#term-single-dispatch) [*обобщённую функцию*](https://python-all.ru/3.4/glossary.html#term-generic-function).207208Чтобы определить обобщённую функцию, декорируйте её декоратором `@singledispatch`. Обратите внимание, что диспетчеризация происходит по типу первого аргумента, поэтому создавайте функцию соответствующим образом:209210```python211>>> from functools import singledispatch212>>> @singledispatch213... def fun(arg, verbose=False):214...     if verbose:215...         print("Let me just say,", end=" ")216...     print(arg)217```218219Чтобы добавить перегруженные реализации к функции, используйте атрибут `register()` обобщённой функции. Это декоратор, принимающий параметр типа и декорирующий функцию, реализующую операцию для этого типа:220221```python222>>> @fun.register(int)223... def _(arg, verbose=False):224...     if verbose:225...         print("Strength in numbers, eh?", end=" ")226...     print(arg)227...228>>> @fun.register(list)229... def _(arg, verbose=False):230...     if verbose:231...         print("Enumerate this:")232...     for i, elem in enumerate(arg):233...         print(i, elem)234```235236Чтобы обеспечить возможность регистрации лямбда-функций и уже существующих функций, атрибут `register()` можно использовать в функциональной форме:237238```python239>>> def nothing(arg, verbose=False):240...     print("Nothing.")241...242>>> fun.register(type(None), nothing)243```244245Атрибут `register()` возвращает недекорированную функцию, что позволяет создавать стеки декораторов, выполнять сериализацию, а также создавать независимые модульные тесты для каждого варианта:246247```python248>>> @fun.register(float)249... @fun.register(Decimal)250... def fun_num(arg, verbose=False):251...     if verbose:252...         print("Half of your number:", end=" ")253...     print(arg / 2)254...255>>> fun_num is fun256False257```258259При вызове обобщённая функция выполняет диспетчеризацию по типу первого аргумента:260261```python262>>> fun("Hello, world.")263Hello, world.264>>> fun("test.", verbose=True)265Let me just say, test.266>>> fun(42, verbose=True)267Strength in numbers, eh? 42268>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)269Enumerate this:2700 spam2711 spam2722 eggs2733 spam274>>> fun(None)275Nothing.276>>> fun(1.23)2770.615278```279280Если для конкретного типа нет зарегистрированной реализации, для поиска более общей реализации используется порядок разрешения методов (MRO). Исходная функция, декорированная `@singledispatch`, зарегистрирована для базового типа `object`, что означает, что она используется, если не найдено более подходящей реализации.281282Чтобы проверить, какую реализацию выберет обобщённая функция для заданного типа, используйте атрибут `dispatch()`:283284```python285>>> fun.dispatch(float)286<function fun_num at 0x1035a2840>287>>> fun.dispatch(dict)    # примечание: реализация по умолчанию288<function fun at 0x103fe0000>289```290291Чтобы получить доступ ко всем зарегистрированным реализациям, используйте атрибут `registry`, доступный только для чтения:292293```python294>>> fun.registry.keys()295dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,296          <class 'decimal.Decimal'>, <class 'list'>,297          <class 'float'>])298>>> fun.registry[float]299<function fun_num at 0x1035a2840>300>>> fun.registry[object]301<function fun at 0x103fe0000>302```303304Новое в версии 3.4.305306#### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`307308Обновляет функцию-*обёртку* так, чтобы она выглядела как *обёрнутая* функция. Необязательные аргументы – кортежи, определяющие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки и какие атрибуты функции-обёртки обновляются соответствующими атрибутами исходной функции. Значения по умолчанию для этих аргументов – константы уровня модуля *WRAPPER\_ASSIGNMENTS* (которая присваивает функции-обёртке атрибуты *\_\_name\_\_*, *\_\_module\_\_*, *\_\_annotations\_\_* и *\_\_doc\_\_*, т.е. строку документации) и *WRAPPER\_UPDATES* (которая обновляет у функции-обёртки атрибут *\_\_dict\_\_*, т.е. словарь экземпляра).309310Чтобы разрешить доступ к исходной функции для интроспекции и других целей (например, обхода кеширующего декоратора вроде [`lru_cache()`](https://python-all.ru/3.4/library/functools.html#functools.lru_cache)), данная функция автоматически добавляет атрибут `__wrapped__` к обёртке, который ссылается на обёртываемую функцию.311312Основное назначение этой функции – использование в [*decorator*](https://python-all.ru/3.4/glossary.html#term-decorator)-функциях, которые оборачивают декорированную функцию и возвращают обёртку. Если функция-обёртка не обновляется, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно не очень полезно.313314[`update_wrapper()`](https://python-all.ru/3.4/library/functools.html#functools.update_wrapper) может использоваться с вызываемыми объектами, не являющимися функциями. Любые атрибуты, указанные в *assigned* или *updated*, отсутствующие у обёртываемого объекта, игнорируются (т.е. эта функция не будет пытаться установить их у функции-обёртки). [`AttributeError`](https://python-all.ru/3.4/library/exceptions.html#AttributeError) по-прежнему возбуждается, если сама функция-обёртка не имеет каких-либо атрибутов, указанных в *updated*.315316Новое в версии 3.2: Автоматическое добавление атрибута `__wrapped__`.317318Новое в версии 3.2: Копирование атрибута `__annotations__` по умолчанию.319320Изменено в версии 3.2: Отсутствующие атрибуты больше не вызывают [`AttributeError`](https://python-all.ru/3.4/library/exceptions.html#AttributeError).321322Изменено в версии 3.4: Атрибут `__wrapped__` теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут `__wrapped__`. (см. [issue 17482](https://python-all.ru/3.4/library/functools.html))323324#### `@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`325326Это удобная функция для вызова [`update_wrapper()`](https://python-all.ru/3.4/library/functools.html#functools.update_wrapper) в качестве декоратора функции при определении функции-обёртки. Она эквивалентна `partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`. Например:327328```python329>>> from functools import wraps330>>> def my_decorator(f):331...     @wraps(f)332...     def wrapper(*args, **kwds):333...         print('Calling decorated function')334...         return f(*args, **kwds)335...     return wrapper336...337>>> @my_decorator338... def example():339...     """докстринг"""340...     print('Called example function')341...342>>> example()343Calling decorated function344Called example function345>>> example.__name__346'example'347>>> example.__doc__348'Docstring'349```350351Без использования этой фабрики декораторов имя примерной функции было бы `'wrapper'`, а строка документации исходной `example()` была бы потеряна.352353## 10.2.1. [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial) Объекты354355[`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial) объекты – это вызываемые объекты, создаваемые с помощью [`partial()`](https://python-all.ru/3.4/library/functools.html#functools.partial). Они имеют три атрибута только для чтения:356357#### `partial.func`358359Вызываемый объект или функция. Вызовы объекта [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial) будут перенаправлены к [`func`](https://python-all.ru/3.4/library/functools.html#functools.partial.func) с новыми аргументами и ключевыми словами.360361#### `partial.args`362363Крайние слева позиционные аргументы, которые будут добавлены в начало позиционных аргументов, передаваемых при вызове объекта [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial).364365#### `partial.keywords`366367Ключевые аргументы, которые будут предоставлены, когда объект [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial) будет вызван.368369[`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial) объекты подобны объектам `function` в том, что они вызываемы, на них можно создавать слабые ссылки и они могут иметь атрибуты. Есть несколько важных отличий. Например, атрибуты [`__name__`](https://python-all.ru/3.4/reference/import.html#__name__) и `__doc__` не создаются автоматически. Кроме того, объекты [`partial`](https://python-all.ru/3.4/library/functools.html#functools.partial), определённые в классах, ведут себя как статические методы и не преобразуются в связанные методы при поиске атрибута экземпляра.370