Note: Please refer to the first part of the module to get an idea about functions of module functools
. This part will cover annotations
.
import functools
functools.lru_cache(maxsize=128,typed=False)
- It's a decoractor which wraps a function to provide memoizing functionality. It saves upto maxsize
function calls. It uses dictionary behind the scene to cache calls. If maxsize
is None
, then Least Recent Used Cache(LRU Cache)
is functionality is disabled and cache can grow without limit. Cache performs best when LRU is of size power of 2.LRU cache performs best when most recent calls are best estimates of upcoming calls.
If typed
is set to True
then function argument of different types will be stored seprately.Ex: f(3) & f(3.0) will be stored differently.
Decorator adds cache_info()
method to wrapped function which provides named_tuple of hits
,misses
, maxsize
and currsize
. It also provides cache_clear()
function for clearing/invalidating cache.
@functools.lru_cache(maxsize=64)
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
def factorial_without_cache(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
print(factorial_without_cache(4))
print(factorial(4))
%time print(factorial(6))
%time print(factorial_without_cache(6))
%time print(factorial(10))
%time print(factorial_without_cache(10))
factorial.cache_info()
factorial.cache_clear()
factorial.cache_info()
functools.total_ordering
: It's a class decorator. If class has defined one or more comparison methods then it adds rest of them. Class must define one of (__lt__()
, __gt__()
, __le__()
,__ge__()
) and __eq__()
compulsarily.@functools.total_ordering
class Employee(object):
def __init__(self,name,age):
self.name = name
self.age =age
def __eq__(self, other):
return (self.name == other.name) and (self.age == other.age)
def __lt__(self, other):
return self.age < other.age
print(dir(Employee)) ## Notice __le__(), __ge__() and __gt__().
functools.singledispatch
: This decorator will convert the normal function into single dispatch generic function
. By single dispatch
it means that based on the type of the first argument of a function, it’ll determine which generic function to execute.Generic function
refers to a function that performs the same operation on different types of argument based on a type of argument.To overload function for a particular data type register
attribute of function is used. We can pass it datatype as an attribute for data type it needs to be invoked.
@functools.singledispatch
def add(x, y):
print('Inside Main Function')
return x + y
@add.register(dict)
def dict_add(x, y):
print('Inside dict version of Function')
x.update(y)
return x
@add.register(set)
def set_add(x, y):
print('Inside set version of Function')
return x.union(y)
@add.register(str)
def str_add(x, y):
print('Inside string version of Function')
return x + y
@add.register(complex)
def complex_add(x, y):
print('Inside complex version of Function')
return complex(x.real + y.real, x.imag + y.imag)
add([1,2,3],[4,5,6])
add('Coderz','Column')
add({'a':'b'},{'c':'d'})
add(set([1,2,3]), set([3,4,5]))
add(5+6j, 6+5j)
When we don't find a particular version for data type then it's method resolution order is used to find appropriate function. Original function decorated with @singledispatch
is registered for object
type which will be executed if no matching function found for a particular data type.
One can find which function will be called for which data type based on dispatch(datatype)
method of function.
print(add.dispatch(dict), add.dispatch(set), add.dispatch(complex), add.dispatch(bool))
add.registry.keys()
print(add.registry[object], add.registry[dict], add.registry[set])
functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
- It's convenience decorator for functools.update_wrapper()
(Functools - functions).def outer2(func):
def wrapper(*args):
print('Before execution')
func(*args)
print('After execution')
return wrapper
def print_title_2(title):
"""
Simple print functionality
"""
print(title)
print_title_wrapped = outer2(print_title_2)
print_title_wrapped('Welcome to CoderzColumn')
print('Attributes not updated even after applying decorator : '+ str(dict(zip(['__module__', '__name__', '__qualname__', '__doc__', '__annotations__','__dict__'],
[print_title_wrapped.__module__,print_title_wrapped.__name__, print_title_wrapped.__qualname__, print_title_wrapped.__doc__, print_title_wrapped.__annotations__, print_title_wrapped.__dict__]))))
def outer(func):
@functools.wraps(func)
def wrapper(*args):
print('Before execution')
func(*args)
print('After execution')
return wrapper
@outer
def print_title(title):
"""
Simple print functionality
"""
print(title)
print()
print_title('Welcome to CoderzColumn')
## Please make a not that document string also got updated otherwise it would be None withuot decorator and function name also got updated from wrapper to print_function.
print('Attributes updated due to decorator : '+ str(dict(zip(['__module__', '__name__', '__qualname__', '__doc__', '__annotations__','__dict__'],
[print_title.__module__,print_title.__name__, print_title.__qualname__, print_title.__doc__, print_title.__annotations__, print_title.__dict__]))))
If you are more comfortable learning through video tutorials then we would recommend that you subscribe to our YouTube channel.
When going through coding examples, it's quite common to have doubts and errors.
If you have doubts about some code examples or are stuck somewhere when trying our code, send us an email at coderzcolumn07@gmail.com. We'll help you or point you in the direction where you can find a solution to your problem.
You can even send us a mail if you are trying something new and need guidance regarding coding. We'll try to respond as soon as possible.
If you want to