# functools

# functools.cache

A decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated.

# functools.reduce(function, sequence, initial=...)

Apply a function of two arguments cumulatively to the items of a sequence, from left to right, so as to reduce the sequence to a single value. For example, functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). The left argument, x, is the accumulated value and the right argument, y, is the update value from the sequence. If the optional initial is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty.

# functools.partial(f, *args, **kwargs)

Return a new partial object which when called will behave like f called with the positional arguments args and keyword arguments kwargs. If more arguments are supplied to the call, they are appended to args. If additional keyword arguments are supplied, they extend and override kwargs.

# Source code

class cache:
    def __init__(self, f):
        self.f = f
        self.cache = {}

    def __call__(self, *args):
        if args not in self.cache:
            self.cache[args] = self.f(*args)
        return self.cache[args]

def reduce(function, sequence, initial=...):
    it = iter(sequence)
    if initial is ...:
        try:
            value = next(it)
        except StopIteration:
            raise TypeError("reduce() of empty sequence with no initial value")
    else:
        value = initial
    for element in it:
        value = function(value, element)
    return value

class partial:
    def __init__(self, f, *args, **kwargs):
        self.f = f
        if not callable(f):
            raise TypeError("the first argument must be callable")
        self.args = args
        self.kwargs = kwargs

    def __call__(self, *args, **kwargs):
        kwargs.update(self.kwargs)
        return self.f(*self.args, *args, **kwargs)