1、本次實驗環境:python
在騰訊雲虛擬主機centos7上配置pyenv多版本python管理器,並安裝交互式web編輯器jupyter,python版本爲3.5.2。
2、裝飾器:mysql
裝飾器的本質是一個函數,接收一個函數做爲參數,而且返回一個函數 帶參數的裝飾器是一個函數,返回一個裝飾器 帶參數的裝飾器最多容許一層,timeit()()(不容許)
在python中,一個函數能夠做爲參數傳遞給另一個函數,還可返回一個函數(不瞭解此請看高階函數)所以,咱們能夠把一個函數傳遞給另外一個函數後,在這個被傳遞的函數的外部補充一些操做(裝飾),然後把這個額外添加了補充裝飾的函數得新return回來,裝飾器也是高階函數的一種。
一、不帶參數的裝飾器:web
#裝飾器的本質是一個函數,接收一個函數做爲參數,而且返回一個函數 #帶參數的裝飾器是一個函數,返回一個裝飾器 #帶參數的裝飾器最多容許一層@timeit()()(不容許) #@abs #@all #@callable #def func(): # pass #abs(all(callable(func))) def fun(fn):#接收一函數做爲參數 print('numner1') def wrap(*args,**kwargs):#接收函數的參數 print('number2') ret = fn(*args,**kwargs)#被裝飾的函數 print('number3') return ret print('number4') return wrap @fun def func(*args,**kwargs): print(args) print('run') return args z = func(1,2,3,4,5,6) print(z) #等價於 def func(*args,**kwargs): print(args) print('run') return args z = fun(func) f = z(1,2,3,4,5,6) print(f) #等價於 def func(*args,**kwargs): print(args) print('run') return args z = fun(func)(1,2,3,4,5,6) print(z) print('############################################') #func被多個裝飾器裝飾 @fun @fun @fun def func(*args,**kwargs): print(args) print('run') return args z = func(1,2,3,4,5,6) print(z) #等價於 print('############################################') def func(*args,**kwargs): print(args) print('run') return args z = fun(fun(fun(func)))(1,2,3,4,5,6) print(z) #根據結果,先執行最外層函數與次內層函數的語句,而後執行次內層與次次內層的語句 #以此類推到最內層函數時,即爲被裝飾的函數func
二、帶參數的裝飾器:redis
#(1)、判斷一個用戶是否在容許列表中,若在,則執行fn功能函數 from functools import wraps def check(allows): def dec(fn): @wraps(fn)#這裏的@wraps(fn)意思是裝飾wrap函數,將fn做爲參數, def wrap(username,*args,**kwargs):#此時函數wrap函數的方法(wrap.__name__等)即爲fn.__name__ if username in allows: return fn(username,*args,**kwargs) return 'not allow'#拋出異常 return wrap return dec @check(['i','you','he']) def test(username): print('congratulations') result = test('he') print(result) #等價於 def test(username): print('congratulations') result = check(['i','you','he'])(test)('she')#高階函數理解了,這裏也就多了一層函數,而這個函數接收了一個參數 print(result)
#(2)、default_user from functools import wraps def inject_user(default_user): def inject(fn): @wraps(fn) def wrap(*args,**kwargs): if 'user' not in kwargs.keys(): kwargs['user'] = default_user ret = fn(*args,**kwargs)#拿到咱們想要的結果 return ret return wrap return inject @inject_user('comyn') def do_something(*args,**kwargs): print(kwargs.get('user')) return 23 ret = do_something(user='magedu') print(ret)
三、裝飾器的應用:sql
(1)、裝飾器本質上是一個函數,該函數用來處理其餘函數,它可讓其餘函數在不須要修改代碼的前提下增長額外的功能 (2)、裝飾器常常用於有切面需求的場景,好比:插入日誌、性能測試、事務處理、緩存、權限校驗等應用場景 (3)、裝飾器是解決這類問題的絕佳設計,有了裝飾器,咱們就能夠抽離出大量與函數功能自己無關的雷同代碼並繼續重用 (4)、歸納的講,裝飾器的做用就是爲已經存在的對象添加額外的功能。
#(1)、cache緩存 import time from functools import wraps from functools import lru_cache#python內置的cache裝飾器 def cache(instance):#instance爲cache接口的封裝 def dec(fn): @wraps(fn) def wrap(*args,**kwargs): #key =>fn_name::params pos = ','.join((str(x) for x in args)) kw = ','.join('{}={}'.format(k,v) for k,v in sorted(kwargs.items())) key = '{}::{}::{}'.format(fn.__name__,pos,kw)#生成key ret = instance.get(key)#判斷key是否在cache中 print(key) print(ret) if ret is not None:#從cache中獲得 return ret ret = fn(*args,**kwargs)#這裏咱們也能夠裝飾一個到騰訊雲mysql數據庫取數據的函數 instance.set(key,ret)#放進緩存(這裏指DictCache) return ret return wrap return dec class DictCache:#這裏用一個字典來做爲緩存,騰訊雲緩存也能夠 def __init__(self): self.cache = dict() def get(self,key): return self.cache.get(key) def set(self,key,value): self.cache[key] = value def __str__(self): return str(self.cache) def __repr__(self): return repr(self.cache) if __name__ == '__main__': cache_instance = DictCache()#緩存實例能夠是騰訊雲memcached,redis,mongodb @cache(cache_instance) def long_time_fun(x): time.sleep(x) return x x = long_time_fun(3) print(x) y = long_time_fun(3) print(y) #拿標準庫來實現 @lru_cache()#具備換出策略lru def time_fun(x): time.sleep(x) return x x = time_fun(3) print(x) y = time_fun(3) print(y)
#(2)、監控 import logging import time from functools import wraps def mertic(prefix,instance):#instance爲各類不一樣類型監控平臺的對象, def timeit(fn):# prefix爲監控對象的前綴,屬於哪一個APP、主機的一個標記 @wraps(fn) def wrap(*args,**kwargs): start = time.time() ret = fn(*args,**kwargs)#取數據 key = '{}.{}.{}'.format(prefix,fn.__module__,fn.__name__) instance.send(key,time.time()-start)#發送到監控處理的地方,好比statsd上 return ret return wrap return timeit #influxdb,grafana展現 class LoggingMetric: def send(self,key,value): logging.warning('{}=>{}'.format(key,value)) @mertic(prefix='mysql',instance=LoggingMetric()) def long_time_fun(x): time.sleep(x) return x print(long_time_fun(1))
(3)、身份驗證(4)、路由
未完待續!!!請看python3.5.2之裝飾器(2)mongodb