Functools

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

cache

Аналог lru_cache(maxsize=None)

cached_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)

У декоратора есть отличия в реализации по сравнению со стандартным @property. Смотри документацию.

Чего-то аналогичного можно достигнуть так:

class DataSet:

    def __init__(self, sequence_of_numbers):
        self._data = tuple(sequence_of_numbers)
    self.__stdev = None

    @property
    def stdev(self):
        if self.__stdew is None:
            self.__stdew = statistics.stdev(self._data)
        return self.__stdew

Значение кеэша можно очистить, удалив атрибут. Декоратор будет работать, только если атрибут с таким же именем, как декорируемое свойство, отсутствует.

cmp_to_key

Интерфыейс сравнения объектов для покрытия методов, сконверченных из python2

lru_cache

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

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

total_ordering

Реализует расширенное сравнение объектов. Данный декоратор получает класс, который предоставляет лишь часть методов сравнения (__lt__(), __le__(), __eq__(), __ne__(), __gt__(), __ge__()) и добавляет остальные.

import functools
import inspect
from pprint import pprint


@functools.total_ordering
class MyObject:

    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        print('  testing __eq__({}, {})'.format(
            self.val, other.val))
        return self.val == other.val

    def __gt__(self, other):
        print('  testing __gt__({}, {})'.format(
            self.val, other.val))
        return self.val > other.val

Если сравнение не может быть выполнено - вернется NotImplemented

partial

Используется в качестве обертки вокруг вызываемого объекта, задавая значения по умолчанию для части или всех его аругментов. С результирующим объектом можно работать как с исходным.

partial не имеет __doc__ и __name__. Их можно задать.

from functools import partial

basetwo = partial(int, base=2)
basetwo.__doc__ = 'Convert base 2 string to an int.'
basetwo('10010')
>>> 18

Или передать из оборачиваемой функции при помощи ipdate_wrapper() (см. дальше)

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

reduce

Получает вызываемый объект и последовательность данных в качестве аргументов. Результат работы функции - последовательный вызов объекта с накоплением результата.

import functools

def do_reduce(a, b):
    print('do_reduce({}, {})'.format(a, b))
    return a + b

data = range(1, 5)
result = functools.reduce(do_reduce, data)
print('result: {}'.format(result))
>>> do_reduce(1, 2)
>>> do_reduce(3, 3)
>>> do_reduce(6, 4)
>>> result: 10

При этом можно задать инициализирующее значение через initializer - с него будет начинаться операция редуцирования. Если функция получила пустой список и инциализатор - она редуцирует к иницализирующему значению. Если в списке только одно значение и нет иницализатора - произойдет редукция к значению в списке. Если передан только пустой список, будет поднята ошибка TypeError

singledispatch

Декоратор позволяет реализовать обобщенную функцию - функция, которая ведет себя по разному, в зависимости от типа переданного аргумента

import functools


@functools.singledispatch
def myfunc(arg):
    print('default myfunc({!r})'.format(arg))


@myfunc.register(int)
def myfunc_int(arg):
    print('myfunc_int({})'.format(arg))


@myfunc.register(list)
def myfunc_list(arg):
    print('myfunc_list()')
    for item in arg:
        print('  {}'.format(item))

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

singledispatchmethod

Аналогично реализует дженерик-методы классов

update_wrapper

Релизует передачу свойств оборачиваемого объекта оберачивающей функции. wraps реализует это же на уровне декоратора. Это позволяет получить имя обернутого объекта и его докстринги

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

[python-standart-library]