functools module learning


1. partial(func, /, *args, **kwargs)

  • Encapsulate the original function and return a partial object object object, which can be called directly
  • Fixing some parameters of the original function is equivalent to adding fixed default values to the original function
    Equivalent to the following code:
def partial(func, /, *args, **kwargs):
    def newfunc(*fargs, **fkwargs):
        newkwargs = {**kwargs, **fkwargs}
        return func(*args, *fargs, **newkwargs)
    newfunc.func = func
    newfunc.args = args
    newfunc.kwargs = kwargs
    return newfunc

For example, you need an int() function that converts binary by default:

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

2. partialmethod(func, /, *args, **kwargs)

  • It is the same as partial, which is specially used in class definition (because the first parameter in class definition needs self/cls by default, partial is not applicable)
  • In the class, no matter ordinary method, static method, classmethod or abstractmethod are applicable

For example:

class Cell:
    def __init__(self):
        self._alive = False
    def alive(self):
        return self._alive
    def set_alive(self, state):
        self._alive = bool(state)
    set_alive = partialmethod(set_state, True)
    set_dead = partialmethod(set_state, False)

>>> c = Cell()
>>> c.alive
>>> c.set_alive()
>>> c.alive

3. update_wrapper(wrapper, warpped, assigned=WRAPPER_ASSIGNMEDTS, updated=WRAPPER_UPDATES)

  • Update the wrapper to look more like the wrapped function
  • It is mainly used in the decorator to wrap the decorated function and return an updated decorating function. If the decorating function is not updated, the metadata of the returned function will come from the decorator rather than the original function
  • Two optional parameters are used to specify which attributes of the original function are assigned directly to the decoration function and which attributes need to be updated by the decoration function. The default values are the module level constants wrapper assignments (assign module, name, qualname, annotations and doc attributes of the decoration function) and wrapper updated (update the attributes of the decoration function)

4. wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updasted=WRAPPER_UPDATES)

  • Simplify the process of calling update ﹐ wrapper, and use it as a decorator
  • Equivalent to partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)

For example:

def my_decorator(f):
    def wrapper(*args, **kwargs):
        print('Calling decorated function')
        return f(*args, **kwargs)
    return wrapper

def example():
    print('Called example function')

>>> example()
Calling decorated function
Called example function
>>> example.__name__
>>> example.__doc__

If wrap is not used, the name of the decorated function will be wrapper, and the document string of the original function example will be lost

5. singledispatch(func)

  • Used as a decorator to convert the decorated function to a generic function
  • Perform different operations according to the type dispatch of the first parameter

For example:

def fun(arg, verbose=False):
    if verbose:
        print('Let me just say,', end='')

def _(arg, verbose=False):
    if verbose:
        print('Strength in numbers, eh?', end='')

def _(arg, verbose=False)
    if verbose:
        print('Enumerate this: ')
    for i, elem in enumerate(arg):
        print(i, elem)

>>> fun('Hello World')
Hello World
>>> fun('test', verbose=True)
Let me just say, test
>>> fun(123, verbose=True)
Strength in numbers, eh? 123
>>> fun(['Issac', 'Chaplin', 'Mr Bean'], verbose=True)
Enumerate this:
0 Issac
1 Chaplin
2 Mr Bean

You can use function type comment instead of explicitly specifying the type above

def _(arg: int, verbose=False):

6. singledispatchmethod(func)

  • Decorate a method as a pan function
  • Perform different operations based on the type dispatch of the first non self or non cls parameter
  • It can be nested with other decorators, but single dispatchmethod, dispatcher.register must be at the outermost layer
  • Other uses are the same as single dispatch

For example:

class Negator:
    def neg(cls, arg):
        raise NotImplementedError("Cannot negate a")
    def _(cls, arg: int):
        return -arg
    def _(cls, arg: bool):
        return not arg

7. cached_property(func)

  • Convert method to a property, similar to @ property
  • The decorated method is only calculated once, and then cached as a common instance property
  • Requires that the instance have a variable \\\\\\\\\\\\\

For example:

class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers
    def stdev(self):
        return statistics.stdev(self._data)
    def variance(self):
        return statistics.variance(self._data)

The bottom layer of the cached property is a non data descriptor. When func is first calculated, the result is saved in a property with the same name of the instance (in the instance's dict). Because the priority of the instance property is greater than the non data descriptor, all subsequent calls only take the instance property directly and do not calculate it again

8. lru_cache(user_function) / lru_cache(maxsize=128, typed=False)

  • Cache the result of the most recent maxsize call of the decorated function
  • If maxsize is set to none, the LRU caching mechanism will not be available, and the cache will grow infinitely. Maxsize's value is preferably n-power of 2
  • Because the underlying uses a dictionary to cache results, the parameters of the decorated function must be hashable
  • Different parameter patterns will be cached separately as unused entries, for example, f(a=1, b=2) and f(b=2, a=1) will be cached twice
  • If typed is set to True, different types of function parameters are cached separately, such as f(3) and f(3.0)

For example:

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)

Keywords: Python

Added by MikeNye on Tue, 14 Apr 2020 10:17:46 +0300