一,什麼是裝飾器python
本質就是函數,功能是爲其餘函數添加附加功能session
原則:閉包
1,不修改被修飾函數的源代碼app
2,不修改被修飾函數的調用方式框架
例子:函數
import time def timmer(func): def wapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('函數的運行時間 %s' % (stop_time - start_time)) return res return wapper @timmer #加裝飾器 def cal(l): #原函數 res = 0 time.sleep(1) for i in l: res += i return res t1 = cal(range(100)) print(t1) '''輸出: 函數的運行時間 1.0000572204589844 4950 '''
二,裝飾器的知識儲備spa
裝飾器 = 高階函數 + 函數嵌套(閉包)code
(一)高階函數blog
定義:ip
1,函數接受的參數是一個函數名
2,函數的返回值是一個函數名
3,知足上述條件任意一個,均可稱之爲高階函數
例子:高階函數
def foo(): print('你好啊林師傅') def test(func): print(func) #打印內存地址 func() #執行函數foo test(foo) #test是高階 '''輸出: <function foo at 0x00000000022F22F0> 你好啊林師傅 '''
例子:函數接受的參數是一個函數名,就能實現給函數增長功能的目標
def foo(): time.sleep(3) print('你好啊林師傅') def test(func): start_time = time.time() func() #執行函數foo stop_time = time.time() print('函數的運行時間 %s' % (stop_time - start_time)) test(foo) #修改了函數原來的調用方式,原來是foo() '''輸出: 你好啊林師傅 函數的運行時間 3.000171661376953 '''
例子:若是再加上返回值是一個函數,則能實現「不修改被修飾函數的調用方式」這一個條件
def foo(): print('from the foo') def test(func): return func # res = test(foo) # print(res) #res獲得的是foo的內存地址 # res() #實際是運行foo foo = test(foo) #若是把函數的執行結果從新賦值給foo函數名 foo() #就能實現「不修改被修飾函數的調用方式」這一個條件 '''輸出: from the foo '''
例子:多運行一次,並不合格(高階函數並不能所有知足裝飾器的須要) 1,不修改foo源代碼,2,不修改foo調用方式
def foo(): time.sleep(3) print('from the foo') def timer(func): start_time = time.time() func() #執行函數foo stop_time = time.time() print('函數的運行時間 %s' % (stop_time - start_time)) return func foo = timer(foo) foo() #又執行了一次函數foo '''輸出: from the foo 函數的運行時間 3.007171869277954 from the foo '''
(二),函數嵌套
定義:在函數裏面定義另外一個函數
def bar(): print('from bar') def foo(): print('from foo') def test(): pass
函數閉包:
閉——封裝變量,包——層層嵌套。其實和函數做用域是一回事。特性:若是內層沒有定義重名變量,外層的變量能夠傳進來。
def father(auth_type): # print('from father %s' %name) def son(): # name='linhaifeng_1' # print('個人爸爸是%s' %name) def grandson(): print('個人爺爺是%s' %auth_type)#實參'filedb'從最外層一直傳導進來 grandson() # print(locals()) son() # father('linhaifeng') father('filedb')
#輸出:個人爺爺是filedb
三,裝飾器框架
使用高階函數+函數閉包完成裝飾器框架。
我的理解:1,裝飾器函數傳入的原函數名func,用在最內層運行原函數,以實現和原函數同樣的效果
2,而在原函數外層包裹的一層函數wrapper,加retunrn wapper返回的是加入新功能後的一個函數地址,在外面運行這個函數地址也就執行了加入的新功能
3,而@這樣一個語法把新函數的地址賦值給原函數名,這樣運行原函數名,就把新的功能增長了。
def timmer(func): #func=test def wrapper(): # print(func) start_time=time.time() func() #就是在運行test() stop_time = time.time() print('運行時間是%s' %(stop_time-start_time)) return wrapper @timmer #test=timmer(test) def test(): time.sleep(3) print('test函數運行完畢') test() '''輸出: test函數運行完畢 運行時間是3.000171661376953 ''' #先使用res來接收 # res=timmer(test) #返回的是wrapper的地址 # res() #執行的是wrapper() #而後使用test原函數名替換res # test=timmer(test) #返回的是wrapper的地址 # test() #執行的是wrapper() #最後python提供@語法等效於test=timmer(test) # @timmer 就至關於 test=timmer(test)
例子:原函數加入返回值狀況
import time def timmer(func): def wrapper(): start_time=time.time() res=func() #使用res接收返回值 stop_time = time.time() print('運行時間是%s' %(stop_time-start_time)) return res #把返回值傳回去 return wrapper @timmer def test(): time.sleep(3) print('test函數運行完畢') return '這是test的返回值' #這裏原函數test加入了返回值 res=test() print(res) '''輸出: test函數運行完畢 運行時間是3.007171869277954 這是test的返回值 '''
例子:加入參數,而且考慮原函數參數不固定的狀況
#傳參的複習和回顧 def test2(name,age,gender): #test2(*('alex',18,'male','x','y'),**{}) #name,age,gender=('alex',18,'male','x','y') print(name) print(age) print(gender) def test1(*args,**kwargs): test2(*args,**kwargs) #args=('alex',18,'male','x','y') kwargs={} test1('alex',18,gender='male') #正確的傳參方式 test1('alex',18,'male') #正確的傳參方式 test1('alex',18,'male','x','y')#多傳了兩個參數,報錯:test2() takes 3 positional arguments but 5 were given
#(*args,**kwargs)實現了傳遞可變參數的功能,在原函數有多個變體的狀況下,裝飾器函數能夠通用 import time def timmer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) #能夠理解爲wrapper獲得的參數原封不動的傳進原函數 print('args 是 %s:' %(args,)) print('kwargs 是: %s' %kwargs) stop_time = time.time() print('運行時間是%s' %(stop_time-start_time)) return res return wrapper @timmer def test(name,age): #兩個參數 time.sleep(1) print('test函數運行完畢,名字是【%s】 年齡是【%s】' %(name,age)) return '這是test的返回值' @timmer def test1(name,age,gender): #三個參數 time.sleep(1) print('test1函數運行完畢,名字是【%s】 年齡是【%s】 性別【%s】' %(name,age,gender)) return '這是test1的返回值' test('linhaifeng',age=18) #args=('linhaifeng') kwargs={'age':18} print('######') test1('alex',18,'male') #args=('alex',18,'male') kwargs={}
'''輸出: test函數運行完畢,名字是【linhaifeng】 年齡是【18】 args 是 ('linhaifeng',): kwargs 是: {'age': 18} 運行時間是1.0000572204589844 ###### test1函數運行完畢,名字是【alex】 年齡是【18】 性別【male】 args 是 ('alex', 18, 'male'): kwargs 是: {} 運行時間是1.0000572204589844 '''
兩個小技巧
#1,解壓序列 a,*_,c =[10,9,8,7,6]#表明全部值 print(a) print(_) #下劃線_是變量 print(c) '''輸出: 10 [9, 8, 7] 6 '''
#2,交換兩個變量值 f1 = 1 f2 = 10 f1,f2 = f2,f1 print(f1,f2) #輸出:10 1
四,裝飾器實戰
(一)使用裝飾器實現驗證和session功能
user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic={'username':None,'login':False} def auth_func(func): def wrapper(*args,**kwargs): if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('用戶名:').strip() passwd=input('密碼:').strip() for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: current_dic['username']=username current_dic['login']=True res = func(*args, **kwargs) return res else: #######這個else很是有價值,不是if的else,而是for的else print('用戶名或者密碼錯誤') return wrapper @auth_func def index(): print('歡迎來到京東主頁') @auth_func def home(name): print('歡迎回家%s' %name) @auth_func def shopping_car(name): print('%s的購物車裏有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) print('before-->',current_dic) index() print('after--->',current_dic) home('產品經理') # shopping_car('產品經理')
五,裝飾器運行流程
1,運行@裝飾器函數名,實際動做是運行裝飾器函數,因爲閉包函數wrapper定義在這個裝飾器函數的裏面,因此僅return wrapper,把wrapper的函數地址返回給原函數名(從新賦值)
2,當程序運行到原函數名(),也就是執行原函數時,由於前面的步驟,實際跳轉到wrapper函數執行。這裏面巧妙的地方是,wrapper函數裏面又包含一個原函數(),這回是真正執行原函數()。
3,執行完原函數後,回到wrapper裏面,原函數()下面的步驟運行。
六,帶參數的裝飾器函數
本質就是在外面再添加一層,把原裝飾器函數包裹起來。原裝飾器函數實現的是把原函數名傳遞進去在內層,帶參數的裝飾器函數實現傳遞功能性參數做用在最外層,層層嵌套。
user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic={'username':None,'login':False} def auth(auth_type='filedb'): def auth_func(func): def wrapper(*args,**kwargs): print('認證類型是',auth_type) if auth_type == 'filedb': if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('用戶名:').strip() passwd=input('密碼:').strip() for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: current_dic['username']=username current_dic['login']=True res = func(*args, **kwargs) return res else: print('用戶名或者密碼錯誤') elif auth_type == 'ldap': print('鬼才特麼會玩') res = func(*args, **kwargs) return res else: print('鬼才知道你用的什麼認證方式') res = func(*args, **kwargs) return res return wrapper return auth_func @auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一個auth_type --->index=auth_func(index) def index(): print('歡迎來到京東主頁') @auth(auth_type='ldap') def home(name): print('歡迎回家%s' %name) # @auth(auth_type='sssssss') def shopping_car(name): print('%s的購物車裏有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) # print('before-->',current_dic) # index() # print('after--->',current_dic) # home('產品經理') shopping_car('產品經理')