裝飾器本質上就是一個python函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外功能,裝飾器的返回值也是一個函數對象。它常常用於有切面需求的場景,好比:插入日誌、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,咱們就能夠抽離大量與函數功能自己無關的雷同代碼並繼續重用。歸納講,裝飾器的做用就是爲依據存在的對象添加額外的功能。
html
在python裏面函數就是一個變量,函數名就是一個變量,這個函數名裏面存的是這個函數的內存地址,它把函數體放到內存裏,在調用的時候從函數名裏面的這個內存地址找到函數體而後運行這個函數。前面的博客說函數的時候,說過函數名後面加上小括號就是調用這個函數,若是隻寫這個函數名的話,打印一下就是這個函數的內存地址。python
1 def test(): 2 print('Hello Word') 3 4 5 print(test) # 打印函數的內存地址 #<function test at 0x100662e18> 6 test() # 調用函數
1 import time 2 def bar(): 3 time.sleep(3) 4 print('in the bar !!') 5 6 def test1(func): 7 start_tiem = time.time() 8 func()# run bar() 9 stop_time = time.time() 10 print('the func run time is %s' %(stop_time-start_tiem)) 11 test1(bar)
1 def fun(): 2 print('in fun ') 3 def bar(): #函數的嵌套 4 print('in the bar ') 5 bar() 6 fun() 7 #--------------------------# 8 name = "Baylor" 9 def change_name(): 10 11 name = "Baylor2" 12 def change_name2(): 13 name = "Baylor3" 14 print("第3層打", name) 15 16 change_name2() # 調用內層函數 17 print("第2層打印", name) 18 19 change_name() 20 print("最外層打印", name)
嵌套函數中的做用域順序-由內而外的尋找符合要求的參數json
但每一次都須要重新對 test1 進行函數賦值,咱們只須要經過裝飾器的特殊書寫方式,在須要增長新功能的函數上增長「@裝飾器函數名」就能夠完成裝飾器的使用 緩存
1 def timer(func): 2 def deco():#函數的嵌套 timer(test1) func = test1 3 start_time = time.time() 4 func() #run test1 5 stop_time = time.time() 6 print(' the func time is %s'%(stop_time - start_time)) 7 return deco #高階函數- 返回值中包含函數名 8 9 @timer # test1 = timer(test1) 10 def test1(): 11 time.sleep(3) 12 print('in the test1') 13 @timer 14 def test2(): 15 time.sleep(2) 16 print('in the test2') 17 18 #test1 = timer(test1) 19 test1()# --->deco 20 test2() 21 print(timer(test1)) #打印test1 內存地址
因此咱們說:高階函數+嵌套函數 =>裝飾器 ,咱們經過高階函數+函數嵌套實現了以上的裝飾器,他符合不改變調用方式,爲函數增長新功能。數據結構
問題:被裝飾的函數若是有參數呢?app
1 def w1(func): 2 def inner(arg): 3 # 驗證1 4 # 驗證2 5 # 驗證3 6 return func(arg) 7 return inner 8 9 @w1 10 def f1(arg): 11 print ('f1') 12 13 14 def w1(func): 15 def inner(arg1,arg2,arg3): 16 # 驗證1 17 # 驗證2 18 # 驗證3 19 return func(arg1,arg2,arg3) 20 return inner 21 22 @w1 23 def f1(arg1,arg2,arg3): 24 print ('f1')
問題:能夠裝飾具備處理n個參數的函數的裝飾器?函數
1 def w1(func): 2 def inner(*args, **kwargs): 3 # 驗證1 4 # 驗證2 5 # 驗證3 6 print(*args,**kwargs) 7 8 return func(*args, **kwargs) 9 return inner 10 11 @w1 12 def f1(arg1, arg2, arg3): 13 print('f1') 14 15 f1(1,2,3)
若是要裝飾的函數帶有參數時,由於你也不知道到底被裝飾的函數會傳什麼參數,因此可使用可變參數和關鍵字參數來接收全部的參數性能
1 import time 2 def timer(func): 3 def deco(*args,**kwargs):#函數的嵌套 timer(test1) func = test1 4 start_time = time.time() 5 func(*args,**kwargs) #run test1 6 stop_time = time.time() 7 print(' the func time is %s'%(stop_time - start_time)) 8 return deco #高階函數 9 10 @timer # test1 = timer(test1) 11 def test1(): 12 time.sleep(3) 13 print('in the test1') 14 @timer # test2 = timer(test2) = deco test2(name) = deco(name) 15 def test2(name,arg): 16 time.sleep(2) 17 print('in the test2 %s %s',name,arg) 18 19 test1()# --->deco 20 test2('Baylor',28)
1 import time 2 user,passwd = 'jinyu','abc123' 3 def auth(auth_type): 4 def outer_wrapper(func): 5 print('auth func:',auth_type) 6 def wrapper(*args,**kwargs): 7 print('------',auth_type) 8 if auth_type =='local': 9 username = input('Username:') 10 password = input('Password') 11 if user == username and passwd == password: 12 13 print('\033[31;1mUser has password authentication\033[0m') 14 # res = func(*args,**kwargs) 15 res = func(*args,**kwargs) #1,2 16 print('-----afte authentication-----',res) 17 res.append(3) 18 print(res) 19 return res,'方法已運行完畢' # 結果 20 else: 21 print('\033[32;1m Invalid username or password\033[0m') 22 elif auth_type =='ldap': 23 print('哈哈哈哈哈哈!!!ldap') 24 return wrapper 25 return outer_wrapper 26 27 def index(): 28 print('welcome to index page!') 29 @auth(auth_type = 'local')#home = wraapper 30 def home(): 31 print('welcome to home page!') 32 return [1,2] 33 @auth(auth_type = 'ldap') 34 def bbs(): 35 print('welcome to bbs page!') 36 37 index() 38 home() 39 # bbs()
三層函數造成了終結裝飾器,經過裝飾器所傳的參數可以進行裝飾器功能判斷,提供了更好的擴展性 測試
1 a = [i*2 for i in range(10)] #列表生成式 3行代碼一行搞定 2 print(a) 3 4 a = [] 5 for i in range(10): 6 a.append(i*2) 7 print(a)
1 a =(i * 2 for i in range(10)) #若是要打印generator中的元素須要藉助next方法 須要循環才能取出 2 # a =[i * 2 for i in range(10)] # list的元素咱們能夠一個個打印出, 3 print(a) #<generator object <genexpr> at 0x10228f2b0> 4 print(next(a)) 5 print(next(a)) 6 print(a.__next__())
7 print(a.__next__())
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a + b # t(b,a+b) a=t[0]-1 b=t[1]-2 a+b等同於 t的下標相加 6 n = n + 1 7 return 'done' # 無用 8 9 10 fib(10) 11 12 # 若是把這個改進成 yield 生成器,經過 __next__調用 只須要 把print(b) 改爲 yield b 便可 13 temp = [] 14 15 16 def fib(max): 17 n, a, b = 0, 0, 1 18 while n < max: 19 yield b 20 a, b = b, a + b # t(b,a+b) a=t[0]-1 b=t[1]-2 a+b等同於 t的下標相加 21 n = n + 1 22 return 'done' # 無用 23 24 x = fib(10) 25 print(x) 26 print(x.__next__()) 27 print(x.__next__()) 28 print(x.__next__()) 29 print(x.__next__())
1 #匿名函數就是不須要顯式的指定函數 2 3 # 這段代碼 4 def calc(n): 5 return n * n 6 7 print(calc(10)) 8 9 # 換成匿名函數 10 calc = lambda n: n * n 11 print(calc(10)) 12 #你也許會說,用上這個東西沒感受有毛方便呀, 。。。。呵呵,若是是這麼用,確實沒毛線改進,不過匿名函數主要是和其它函數搭配使用的呢,以下 13 #x=[1, 5, 7, 4, 8] 14 res = map(lambda x: x * 2, [1, 5, 7, 4, 8]) #map會根據提供的函數對指定序列作映射。 15 for i in res: 16 print(i)
#匿名函數只能處理比較簡單的處理邏輯,只能寫簡單的表達式,不能寫循環 判斷,好比三元運算符
json是一種全部語言中都通用的key-value數據結構的數據類型,很像python中的字典,json處理使用json模塊,json模塊有下面經常使用的方法:ui
1 import json 2 3 dic = {"name": "niuniu", "age": 18} 4 print(json.dumps(dic)) # 把字典轉成json串 5 fj = open('a.json', 'w') 6 print(json.dump(dic, fj)) # 把字典轉換成的json串寫到一個文件裏面 7 s_json = '{"name":"niuniu","age":20,"status":true}' 8 print(json.loads(s_json)) # 把json串轉換成字典 9 10 # 先建立b.json文件 運行運行此代碼*{"name": "chenjianguo", "age": 18}* 11 fr = open('b.json', 'r') 12 print(json.load(fr)) # 從文件中讀取json數據,而後轉成字
內置函數詳細介紹 https://docs.python.org/3/library/functions.html?highlight=built#ord
1 import time 2 def consumer(name): 3 print('%s 我準備吃包子了'%name) 4 while True: 5 baozi = yield #保存當前狀態返回, 6 print('包子[%s]來了,被[%s]吃了'%(baozi,name)) 7 # c = consumer('houzi') 8 # c.__next__() 9 #b = '韭菜餡' 10 #c.send(b) # 調用yield 並傳值 11 #c.__next__() 12 def producer(name): 13 c = consumer('A') #生成 14 c2 = consumer('B') 15 c.__next__() 16 c2.__next__() 17 print('開始準備作包子了。。。。。。') 18 for i in range(10): 19 time.sleep(1) 20 print('作了2個包子!!!!') 21 c.send(i) 22 c2.send(i) 23 24 producer('houzi')
分享一下,須要用類裏面的__call__方法,__call__方法就是能夠把這個實例當成一個函數來調用,若是正常使用類裏面的方法的話,實例方法要先實例化類,而後才能調用,靜態方法、類方法則須要用類名或者實例化以後的對象來調用,而實例化以後的這個對象,是不能被調用的,__call__方法就是把這個實例變成一個能夠調用的對象,也就是說實例化以後,這個對象就能夠和一個普通函數同樣被調用。
1 class Foo: 2 def __call__(self, *args, **kwargs): 3 print('call....') 4 def test(self):# 5 print('test....') 6 if __name__ == '__main__': 7 t = Foo()#實例化類 8 t.test()#正常調用實例方法 9 t()#直接調用實例化以後的對象
理解了上面的以後,就可使用class來寫一個裝飾器了,計算程序的運行時間,固然思想和之前用函數寫裝飾器是同樣的
1 class Fuck(object): 2 def __init__(self, func): 3 self.func = func 4 def __call__(self, *args, **kwargs): 5 import time 6 start_time = time.time() 7 res = self.func(*args, **kwargs) 8 end_time = time.time() 9 print('the function "%s" run time is %s' % (self.func.__name__, 10 (end_time - start_time))) 11 return res 12 @Fuck 13 def run(name): 14 import time 15 time.sleep(1) 16 return 'sb_%s' % name 17 print(run('hyf'))