# Python Functools Module

The `functools` module is for higher-order functions: functions that act on or return other functions. In general, any callable object can be treated as a function for the purposes of this module.

## Importing the Module

```python
import functools
```

## Caching (`lru_cache`)

Decorator to wrap a function with a memoizing callable that saves up to the `maxsize` most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.

```python
import functools

@functools.lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))
print(fibonacci.cache_info())
```

## Partial Functions (`partial`)

Return a new partial object which when called will behave like the original function called with the positional arguments args and keyword arguments keywords.

```python
import functools

def power(base, exponent):
    return base ** exponent

# Create a new function that squares a number
square = functools.partial(power, exponent=2)

print(square(5)) # Output: 25
```

## Reduce (`reduce`)

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.

```python
import functools

# Calculate the product of a list
items = [1, 2, 3, 4, 5]
product = functools.reduce(lambda x, y: x * y, items)

print(product) # Output: 120 (1*2*3*4*5)
```

## Decorator Metadata (`wraps`)

This is a convenience function for invoking `update_wrapper()` as a function decorator when defining a wrapper function. It copies the docstring, name, and other metadata from the wrapped function to the wrapper.

```python
import functools

def my_decorator(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        print('Calling decorated function')
        return f(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print('Called example function')

print(example.__name__) # Output: example (without @wraps it would be 'wrapper')
print(example.__doc__)  # Output: Docstring
```

## Total Ordering (`total_ordering`)

Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest.

```python
import functools

@functools.total_ordering
class Student:
    def __init__(self, grade):
        self.grade = grade

    def __eq__(self, other):
        return self.grade == other.grade

    def __lt__(self, other):
        return self.grade < other.grade

s1 = Student(90)
s2 = Student(80)

print(s1 > s2) # True (automatically generated from __lt__)
```

[[programming/python/python]]