Spec-Zone.ru › Python 3.5

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

Примеры сортировки и краткий учебник по сортировке см. в Руководстве по сортировке.

Введено в версии 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 для статического веб-контента:

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

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

Для определения обобщенной функции используйте декоратор @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() была бы потеряна.

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.5/library/functools.html

Spec-Zone.ru

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