Spec-Zone.ru › Python 3.6

functools — Функции высшего порядка и операции над вызываемыми объектами

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

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

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

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

Примеры сортировки и краткий учебник по сортировке см. в Сортировка HOW TO.

Новая в версии 3.2.

@functools.lru_cache(maxsize=128, typed=False)

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

Поскольку для кеширования используются словари, позиционные и именованные аргументы функции должны быть хешируемыми.

Если maxsize установлено в None, функция кеширования LRU отключена, и кеш может расти без ограничений. Функция кеширования LRU работает лучше всего, когда maxsize является степенью двойки.

Если typed установлено в true, функции аргументов разных типов будут кешироваться отдельно. Например, f(3) и f(3.0) будут рассматриваться как отдельные вызовы с отдельными результатами.

Для измерения эффективности кеша и настройки параметра maxsize обернутая функция снабжена функцией cache_info(), которая возвращает именованную кортеж, отображающий hits, misses, maxsize и currsize. В многопоточной среде значения hits и misses являются приблизительными.

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

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

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

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

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

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
    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.

@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()))

Примечание

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

Новая в версии 3.2.

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

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

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

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

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

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
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(object):
...     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[, initializer])

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

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

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value
END_OF_DOCUMENT_MARKER
@functools.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(int)
... def _(arg, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> @fun.register(list)
... def _(arg, verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

Для возможности регистрации лямбда-выражений и уже существующих функций можно использовать атрибут register() в функциональной форме:

>>> def nothing(arg, verbose=False):
...     print("Nothing.")
...
>>> fun.register(type(None), nothing)

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

>>> @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, что означает, что она используется, если не найдена лучшая реализация.

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

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

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

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

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

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

Новая в версии 3.2: Автоматическое добавление атрибута __wrapped__.

Новая в версии 3.2: Копирование атрибута __annotations__ по умолчанию.

Изменено в версии 3.2: Отсутствующие атрибуты больше не вызывают AttributeError.

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

@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() была бы потеряна.

10.2.1. Объекты partial

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

partial.func

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

partial.args

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

partial.keywords

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

partial объекты похожи на function объекты тем, что они вызываемые, слабые, и у них могут быть атрибуты. Существуют некоторые важные различия. Например, атрибуты __name__ и __doc__ не создаются автоматически. Также, объекты partial, определённые в классах, ведут себя как статические методы и не преобразуются в связанные методы во время поиска атрибутов экземпляра.

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/functools.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API