8.14. Decorator Stdlib Functools¶
8.14.1. Wraps¶
from functools import wraps
@wraps(func)
Without Wraps:
>>> 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
With Wraps:
>>> 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
8.14.2. Cached Property¶
from functools import cached_property
@cached_property(method)
>>> 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
2.1794494717703365
>>>
>>> flower.mean
2.55
8.14.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)
8.14.4. Assignments¶
"""
* Assignment: Decorator Functools Func
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
English:
1. Use `functools.wraps` in correct place
2. Run doctests - all must succeed
Polish:
1. Użyj `functools.wraps` w odpowiednim miejscu
2. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction
>>> assert isfunction(mydecorator), \
'Create mydecorator() function'
>>> assert isfunction(mydecorator(lambda: ...)), \
'mydecorator() should take function as an argument'
>>> @mydecorator
... def hello():
... '''Hello Docstring'''
>>> hello.__name__
'hello'
>>> hello.__doc__
'Hello Docstring'
"""
from functools import wraps
# type: Callable[[Callable], Callable]
def mydecorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
"""
* Assignment: Decorator Functools Args
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
English:
1. Use `functools.wraps` in correct place
2. Run doctests - all must succeed
Polish:
1. Użyj `functools.wraps` w odpowiednim miejscu
2. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction
>>> assert isfunction(mydecorator), \
'Create mydecorator() function'
>>> assert isfunction(mydecorator(True)), \
'mydecorator() should take one positional argument'
>>> assert isfunction(mydecorator(happy=True)), \
'mydecorator() should take one keyword argument'
>>> assert isfunction(mydecorator(happy=True)(lambda: ...)), \
'The result of mydecorator() should take function as an argument'
>>> @mydecorator(happy=False)
... def hello():
... '''Hello Docstring'''
>>> hello.__name__
'hello'
>>> hello.__doc__
'Hello Docstring'
"""
from functools import wraps
# type: Callable[[bool], Callable]
def mydecorator(happy=True):
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
"""
* Assignment: Decorator Functools Cls
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min
English:
1. Modify code to restore docstring and name from decorated class
2. Run doctests - all must succeed
Polish:
1. Zmodyfikuj kod aby przywrócić docstring oraz nazwę z dekorowanej klasy
2. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction, isclass
>>> assert isfunction(mydecorator), \
'Create mydecorator() function'
>>> assert mydecorator(object), \
'mydecorator() should take class as an argument'
>>> assert isclass(mydecorator(object)), \
'The result of mydecorator() should be a class'
>>> @mydecorator
... class Hello:
... '''Hello Docstring'''
>>> hello = Hello()
>>> hello.__name__
'Hello'
>>> hello.__doc__
'Hello Docstring'
"""
# type: Callable[[Type], Type]
def mydecorator(cls):
class Wrapper(cls):
pass
return Wrapper