Decorators
Decorators¶
- Function Decorator
- Class Decorator
Decorators allow us to modify existing classes or functions without redefining them.
- Practical decorators | YouTube.com Reuven M. Lerner
In [0]:
from functools import wraps
from inspect import signature
def logger(func):
""" Function Decorator for Logging """
@wraps(func) # preserves meta data of the func
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
arg_obj = signature(func).bind(*args, **kwargs).arguments.items()
print(f"LOG: {func.__name__}", end='(')
print(', '.join(f'{k}={v}' for k, v in arg_obj), end=') ')
print(f"is {result}")
return result
return wrapper
Logger Usage¶
In [0]:
# Before: No Logging
def mul(x, y):
""" Multiplies x by y. """
return x * y
var = mul(2, 3)
In [0]:
# Nothing Printed
In [0]:
# After: Logging Enabled
@logger
def mul(x, y):
""" Multiplies x by y. """
return x * y
var1 = mul(2, 3)
var2 = mul(4, y=3)
var3 = mul(x=6, y=5)
Help with @wraps
decorator.¶
help(mul)
Help on function mul in module __main__:
mul(x, y)
Multiplies x by y.
Help without @wraps
decorator.¶
help(mul)
Help on function wrapper in module __main__:
wrapper(*args)
# @wraps(func)
In [0]:
import pickle
class Cache:
""" Function Cache """
def __init__(self, function):
self.cache = {}
self.function = function
self.__doc__ = function.__doc__
def __call__(self, *args, **kwargs):
key = (pickle.dumps(args), pickle.dumps(kwargs))
if key in self.cache:
result = self.cache[key]
else:
result = self.cache[key] = self.function(*args, **kwargs)
print(f"Cached Value: {result}")
return result
In [0]:
@Cache
def fib(n):
""" Recursive Fibonacci """
return 1 if n < 3 else fib(n - 1) + fib(n - 2)
print(fib(10))
print(fib(12)) # Notice the cache only caches new values.
In [0]: