裝飾器本質上是一個函數,(裝飾其餘函數)就是爲其餘函數添加附加功能
裝飾器是對它被裝飾的函數是透明的。
如今有以下函數:python
def test1(): print "in the test1" def test2(): print "in the test2" test1() test2()
我如今增長一個記錄日誌的功能,只須要增長一個打印日誌的函數,再test1和test2中調用它就ok:app
def test1(): print "in the test1" logger() def test2(): print "in the test2" logger() def logger(): print "loging" test1() test2()
如今咱們來講裝飾器,假如剛纔的代碼,我不修改test1和test2函數裏邊的內,還要打印日誌的功能,這就能夠用裝飾器來完成這麼一個額外的功能,同時還不能修改test1和test2的內容。
因此寫裝飾器就有這麼兩個原則:
1.不能修改被裝飾函數的源代碼。
2.不能修改被裝飾函數的調用方式。 ide
def timmer(func): def warpper(): start_time = time.time() func() end_time = time.time() print "function %s run time is %s"%(func.__name__,end_time - start_time) return warpper #如上代碼就是一個裝飾器,用來計算函數運行時間 @timmer def test1(): time.sleep(3) print "in test1" test1()
執行結果以下
咱們就實現了一個統計test1函數運行時間的功能。對於test1來講並無修改它的源代碼,它根本感知不到這個裝飾器函數的存在,因此說裝飾器對於它來講是透明的。只是在函數定義的地方加上@+裝飾器名稱(裝飾器自己就是一個函數)。在調用test1這個函數時他就會執行裝飾器的額外功能。函數
這是一個什麼樣的原理呢?
這裏@符號python解釋器的一個關鍵字,這個關鍵字表明在一個函數中調用另外一個函數。在這裏呢就是timmer函數調用test1而後再賦值給test1。其實@timmer在這裏等價於test1 = timmer(test1) 再回到以前咱們說裝飾器定義有兩個原則不修改被裝飾函數的源代碼,不修改被裝飾函數的調用方式,這裏咱們並無修改test1的內容,而調用test1也跟以前同樣,因此裝飾器就是高階函數+嵌套函數。優化
帶參數的裝飾器網站
#!/usr/bin/env python #-*- coding:utf-8 -*- import time def timmer(func): def warpper(*args,**kwargs): start_time = time.time() func(*args,**kwargs) end_time = time.time() print "function %s run time is %s"%(func.__name__,end_time - start_time) return warpper #如上代碼就是一個裝飾器,用來計算函數運行時間 @timmer def test1(name): time.sleep(3) print "in test1" print name test1("abner")
假如一個網站有不少個頁面,每一個頁面都要判斷當前的用戶。那認證信息這塊就是一塊須要複用的地方。日誌
user,passwd = 'zhang','123456' def auth(func): def wrapper(*args,**kwargs): username = raw_input("username :").strip() password = raw_input("password :").strip() if username == user and password == password: print "\003[32:1mUser has passed \003[0m" func(*args,**kwargs) else("auth failed.") return wrapper @auth def index(): print "welcome to index page" @auth def home(): print "welcome to home page" @auth def blog(): print "welcome to blog page" index() home() blog()
這樣已是一個完整的裝飾器了,咱們作了一個簡單的認證。假如咱們基於這一個認證模塊要進一步優化,假設home page要經過ldap認證,blog要經過本地文件認證。那麼如何來實現呢?能夠經過給裝飾器函數傳參告訴它要進行哪一個認證。code
user,passwd = 'zhang','123456' def auth(auth_type): def outerwarppers(func): def wrapper(*args,**kwargs): if auth_type == "local_file" username = raw_input("username :").strip() password = raw_input("password :").strip() if username == user and password == password: print "\003[32:1mUser has passed \003[0m" res = func(*args,**kwarg) returen res else: exit("auth filed") elif: auth_type == "ldap" print("use ldap auth") func(*args,**kwarg) return wrapper return outerwarppers def index(): print "welcome to index page" @auth(auth_type="ldap") def home(): print "welcome to home page" @auth(auth_type="local_file") def blog(): print "welcome to blog page" index() home() blog()