Decorator is one of the powerful tool in meta programming . in python it is very simple to define and use a decorator
1. Function level decorator
Sample #1 function execution time
import time, random
def exe_time(func):
print('before declaration...')
def inner_func(*arg, **kwargs):
arg_str = ','.join(list(map(str,arg)))
print(f'===args: {arg_str}===')
print(f'==keywarded args ==')
for k,v in kwargs.items():
print(f'{k}:{v}') start = time.time()
print('before executing...')
res = func(*arg, **kwargs)
end=time.time()
total = round(end-start,3)
print(f'after executing...total {total}')
return res print('after declaration...')
return inner_func@exe_time
def test(n,n2,p1='a',p2='b'):
res=0
for i in range(n):
wait = random.randint(0,2)*i
res+=wait
time.sleep(wait)
print(f'total wait :{round(res,2)} seconds')
return res
Sample #2 Create a simple memory cache
import functools
import mathdef cache_res(func):
def inner(*arg, **kwarg):
key = arg+tuple(kwarg.items())
if key not in inner.cache:
inner.cache[key] = func(*arg, **kwarg)
return inner.cache[key]
inner.cache = {}
return inner@cache_res
def test_compute(n):
res= math.sqrt(n)+ math.sin(n) * math.cos(n)
print(f'calculated result of :{n} -- {res}')
return res"""
only calculated 2 times
"""
if __name__ == "__main__":
print(test_compute(10))
print(test_compute(10))
print(test_compute(5))
print(test_compute(5))
print(test_compute(5))
Class Level decorator
Sample#2 “template” id class field
import uuiddef add_id(cls):
init = cls.__init__ def my_init(self, *args, **kwargs):
self.id = uuid.uuid1()
init(self, *args, **kwargs)
cls.__init__ = my_init
return cls@add_id
class person:
def __init__(self,name):
self.name=name def __str__(self):
return f'{self.id},{self.name}'def test_add_id():
p = person('jeo')
print(p)if __name__ == "__main__":
test_add_id()
Sample#2 Call counter
import functoolsclass call_counter:
def __init__(self, f):
functools.update_wrapper(self, f)
self.func = f
self.num = 0
def __call__(self, *args, **kwargs):
self.num +=1
print(f'called {self.num} times')
return self.func(*args, **kwargs)@call_counter
def test_call():
print("testing")if __name__ == "__main__":
test_call()
test_call()
test_call()
Sample #3 Simple flask auth
import functools
from flask import Flask
from flask import requestclass auth:
__name__ = "auth"
def __init__(self, f):
self.tokens = ["12345"]
def __call__(self, *args, **kwargs):
t = request.headers.get('token')
print(t)
if t not in self.tokens:
return 'Unauthenticated'
return self.func(*args, **kwargs)app = Flask(__name__)@app.route('/hello')
def helloIndex():
return 'Hello World from Python Flask!'@app.route('/auth_hello')
@auth
def auth_hello():
return 'Hello with token'app.run(host='0.0.0.0', port=5000)
Browse http://localhost:5000/auth_hello without sending token
Send request with correct token will see hello message .
Sample4 pass parameters to decorator
from functools import wrapsdef post_log(level, name, message, save_result):
def inner_func(func):
@wraps(func)
def wrapper(*arg, **kwargs):
res = func(*arg, **kwargs)
res_str = res if save_result else ''
print(f'{name},{message} {res_str}')
return res
return wrapper
return inner_func@post_log("DEBUG", "name", "message", False)
def func1():
return 1@post_log("INFO", "name", "message", True)
def func2():
return 2if __name__ == "__main__":
print(func1())
print(func2())