7.7. Decorator Stdlib Functools

7.7.1. Wraps

  • from functools import wraps

  • @wraps(func)

def mydecorator(func):
    def wrapper(*args, **kwargs):
        """wrapper docstring"""
        return func(*args, **kwargs)
    return wrapper


@mydecorator
def myfunction(x):
    """myfunction docstring"""
    print(x)


print(myfunction.__name__)
# wrapper

print(myfunction.__doc__)
# wrapper docstring
from functools import wraps


def mydecorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper docstring"""
        return func(*args, **kwargs)
    return wrapper


@mydecorator
def myfunction(x):
    """myfunction docstring"""
    print(x)


print(myfunction.__name__)
# myfunction

print(myfunction.__doc__)
# myfunction docstring

7.7.2. Cached Property

  • from functools import cached_property

  • @cached_property(func)

import statistics
from functools import cached_property


class Iris:
    def __init__(self, *args):
        self._measurements = args

    @cached_property
    def mean(self):
        return statistics.mean(self._measurements)

    @cached_property
    def stdev(self):
        return statistics.stdev(self._measurements)


flower = Iris(5.1, 3.5, 1.4, 0.2)
flower.stdev()
flower.mean()

7.7.3. LRU (least recently used) cache

  • from functools import lru_cache

  • @lru_cache(maxsize=None)

from functools import 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)

7.7.4. Assignments

Code 7.17. Solution
"""
* Assignment: Decorator Functools Func
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min

English:
    1. Use code from "Given" section (see below)
    2. Use `functools.wraps` in correct place
    3. Compare result with "Tests" section (see below)

Polish:
    1. Użyj kodu z sekcji "Given" (patrz poniżej)
    2. Użyj `functools.wraps` w odpowiednim miejscu
    3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> @mydecorator
    ... def hello():
    ...     '''Hello Docstring'''

    >>> hello.__name__
    'hello'
    >>> hello.__doc__
    'Hello Docstring'
"""


# Given
from functools import wraps


def mydecorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper


Code 7.18. Solution
"""
* Assignment: Decorator Functools Args
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min

English:
    1. Use code from "Given" section (see below)
    2. Use `functools.wraps` in correct place
    3. Compare result with "Tests" section (see below)

Polish:
    1. Użyj kodu z sekcji "Given" (patrz poniżej)
    2. Użyj `functools.wraps` w odpowiednim miejscu
    3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> @mydecorator(happy=False)
    ... def hello():
    ...     '''Hello Docstring'''
    >>> hello.__name__
    'hello'
    >>> hello.__doc__
    'Hello Docstring'
"""


# Given
from functools import wraps


def mydecorator(happy=True):
    def decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    return decorator


Code 7.19. Solution
"""
* Assignment: Decorator Functools Cls
* Complexity: easy
* Lines of code: 2 lines
* Time: 5 min

English:
    1. Use code from "Given" section (see below)
    2. Modify code to restore docstring and name from decorated class
    3. Compare result with "Tests" section (see below)

Polish:
    1. Użyj kodu z sekcji "Given" (patrz poniżej)
    2. Zmodyfikuj kod aby przywrócić docstring oraz nazwę z dekorowanej klasy
    3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> @mydecorator
    ... class Hello:
    ...     '''Hello Docstring'''
    >>> hello = Hello()
    >>> hello.__name__
    'Hello'
    >>> hello.__doc__
    'Hello Docstring'
"""


# Given
def mydecorator(cls):
    class Wrapper(cls):
        pass
    return Wrapper