Python基礎05 - 裝飾器
@@@文章內容參照老男孩教育 Alex金角大王,武Sir銀角大王@@@
函數即對象
在python中,函數和我們之前的[1,2,3],‘abc‘,8 等一樣都是對象,而且函數是最高級的對象(對象是類的實例化,可以調用相應的方法,函數是包含變量對象的對象。)
高階函數
嵌套函數及閉包
1 def func(): 2 x = 1 3 def foo(): 4 print(x) 5 return foo 6 7 a = func() 8 a()
閉包(closure)是函數式編程的重要的語法結構
定義:如果在一個內部函數裏,對在外部作用域(但不是在全局作用域)的變量進行引用,那麽內部函數就被認為是閉包
如上實例,foo就是內部函數,foo裏引用了外部作用域的變量x(x在外部作用域func裏面,不是在全局作用域),則這個內部函數foo就是一個閉包。
代碼的開放封閉原則
軟件開發中的一個原則“開放-封閉”原則,簡單來說,它規定已經實現的功能代碼不允許被修改,但可以被擴展,即:
- 封閉:已實現的功能代碼塊不應該被修改
- 開放:對現有功能的擴展開發
裝飾器概念
裝飾器本質上是一個函數,該函數用來處理其他函數,它可以讓其他函數在不需要修改代碼的前提下增加額外的功能,裝飾器的返回值也是一個函數對象。它經常用於有切面需求的場景,比如:插入日誌、性能測試、事務處理、緩存、權限校驗等應用場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。
1 import time 2 # 業務生產中大量調用的函數 3 def foo(): 4 print(‘hello foo‘) 5 foo() 6 # 現在有一個新的需求,希望可以記錄下函數的執行時間,於是在代碼中添加日誌代碼 7 def foo(): 8 start_time = time.time() 9 time.sleep(2) 10 print(‘hello foo‘) 11 stop_time =time.time() 12 print(‘spend %s‘ %(stop_time - start_time))13 14 foo() 15 16 # bar()、bar2()也有類似的需求,怎麽做?再在bar函數裏調用時間函數? 17 # 這樣就造成大量雷同的代碼,為了減少重復寫代碼,我們可以這樣做,重新定義一個函數:專門設定時間 18 def show_time(func): 19 start_time = time.time() 20 func() 21 stop_time = time.time() 22 print(‘spend %s‘ %(stop_time - start_time)) 23 24 def foo(): 25 time.sleep(2) 26 print(‘hello foo‘) 27 28 show_time(foo)
邏輯上不難理解,而且運行正常。但是這樣改變了調用方式,容易被人投訴,因為我們每次都要將一個函數作為參數傳遞給show_time函數。而且這種方式已經破壞了原有的代碼邏輯結構,之前執行業務邏輯進,執行foo(),但是現在不得不改成show_time(foo)。那麽有沒有更好的方式呢?當然有,答案就 是裝飾器。
簡單裝飾器
1 import time 2 def show_time(func): 3 def wrapper(): 4 start_time = time.time() 5 func() 6 stop_time = time.time() 7 print(‘spend %s‘ %(stop_time - start_time)) 8 9 return wrapper 10 11 # @符號是裝飾器的語法堂,在定義函數的時候使用,避免再一次賦值操作 12 @show_time # foo = show_time(foo) 13 def foo(): 14 time.sleep(2) 15 print(‘hello foo‘) 16 17 foo()
裝飾器在python使用如此方便都要歸因於python函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被賦值給其他變量,可以作為返回值,可以被定義在另外一個函數內。
帶參數的被裝飾函數
1 import time 2 def show_time(func): 3 def wrapper(a,b): 4 start_time = time.time() 5 ret = func(a,b) 6 stop_time = time.time() 7 print(‘spend %s‘ %(stop_time - start_time)) 8 return ret 9 10 return wrapper 11 12 @show_time 13 def add1(a,b): 14 time.sleep(1) 15 print(a + b) 16 17 add1(4,5) 18 19 # 註意函數的返回值時 20 @show_time 21 def add2(a,b): 22 time.sleep(1) 23 return a + b 24 25 print(add2(4,5))
不定長參數
1 import time 2 def show_time(func): 3 def wrapper(*args,**kwargs): 4 start_time = time.time() 5 ret = func(*args,**kwargs) 6 stop_time = time.time() 7 print(‘spend %s‘ %(stop_time - start_time)) 8 return ret 9 10 return wrapper 11 12 @show_time 13 def add(*args,**kwargs): 14 time.sleep(1) 15 sum = 0 16 for i in args: 17 sum += i 18 print(sum) 19 20 add(4,5,6,7,8,9)
帶參數的裝飾器
裝飾器還有更大的靈活性,例如帶參數的裝飾器;在上面的裝飾器調用中,比如@show_time,該裝飾器唯一的參數就是執行業務的函數。裝飾器的語法允許我們在調用時,提供其它參數,比如@auth(a)。這樣就為裝飾器的編寫和使用提供了更大的靈活性。
1 user,passwd = ‘abc‘,‘abc123‘ # 存用戶信息 2 user_status = False # 用戶登錄了就把這個改成True 3 4 def auth(auth_ytpe=0): # 把要執行的模塊從這裏傳進來 5 def wrapper(func): 6 def inner(*args,**kwargs): # 7 if auth_ytpe == ‘1‘: 8 global user_status 9 if user_status == False: 10 username = input(‘Username:‘).strip() 11 password = input(‘Password:‘).strip() 12 if username == user and password == passwd: # 用戶認證 13 print(‘welcome login ......‘) 14 user_status = True # 登入成功改成True 15 else: 16 print(‘wrong username or password!‘) 17 if user_status == True: 18 return func(*args,**kwargs) # 驗證通過,調用相應功能函數 19 elif auth_ytpe == ‘2‘: 20 pass 21 return inner 22 return wrapper 23 24 @auth(0) 25 def index(): 26 print(‘welcome to index‘) 27 28 @auth(‘1‘) 29 def home(a): 30 print(‘welcome to home %s‘%(a)) 31 return home 32 33 @auth(‘2‘) 34 def bbs(): 35 print(‘welcome to bbs‘) 36 37 index() 38 print(home(‘33‘)) 39 bbs()
多層裝飾器
1 def w1(fnuc): 2 def wrapper(): 3 return ‘<b>‘ + fnuc() + ‘</b>‘ 4 5 return wrapper 6 7 def w2(fnuc): 8 def wrapper(): 9 return ‘<i>‘ + fnuc() + ‘</i>‘ 10 11 return wrapper 12 13 @w1 14 @w2 15 def hello(): 16 return ‘hello‘ 17 18 print(hello())
Python基礎05 - 裝飾器