[TOC]
裝飾器
閉包函數
思考練習
def addx(x):
def adder(y):return x+y
return adder
c = addx(8)
print(c(10))
要計算這段代碼的結果,首先需要具備“python中一切皆對象”的思想。在python中,函數也是一樣可以被賦值傳參加入運算的。這就成為閉包函數實現前提,因為閉包的梗概(我理解)就在于:定義一個函數,并且返回值為子函數方法。
上述代碼中addx(x)
是一個需要傳參x進入的函數,并且有一個子函數adder(y)
同樣需要被傳參y。addx的返回值就是adder方法。
我們都知道函數名稱后面跟‘()’就是運行函數,函數通常會運算并且得出一個值,當你暫時不需要這個值的時候,你可以先return func
,而不添加(),這樣做的意義就在于,保留返回了這個函數運算的方法,在需要傳參進入時候再進行傳參。
當然也可以直接輸出這個func,這樣的結果就是這個函數在內存的存儲位置。
上述代碼中,c = addx(8)
完成了addx中對x的傳參,傳入x之后adder(y)
返回的語句就相當于8+y
,并且adder返回給addx的方法,仍然是adder(y)
所以分解輸出式子:
c(10)==addx(8)(10)==【adder(10)且已知x = 8】==8+10==18
為什么要使用裝飾器
python中裝飾器的多種用途:
輸出日志、參數檢查、代理設置、計數計時、結果緩存等。
裝飾器可以在不影響原代碼的前提下,為原代碼輸出結果附加一些內容
開放封閉原則:軟件實體應該對擴展開放,而對修改封閉。開放封閉原則是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化,降低耦合,而開放封閉原則正是對這一目標的最直接體現。
開放封閉原則主要體現在兩個方面:
對擴展開放,意味著有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。
對修改封閉,意味著類一旦設計完成,就可以獨立其工作,而不要對類盡任何修改。
裝飾器范例代碼:
(一)老師教的定義方式
import time #載入time函數庫
def timer(func): #定義裝飾器名稱
def wrapper(): #定義用于返回閉包的內建函數
start_time = time.time()
func()
end_time = time.time()
print('func run time is %s'%(end_time-start_time))
return wrapper #返回閉包函數的方法,后面添加括號的話,就執行這個方法并且獲得結果
@timer #語法糖,注意這個@語法要在‘def’前面使用
def index():
time.sleep(1)
print('this is a test func')
index()
(二)網上的花活
# 構建裝飾器
def logging(func):
@functools.wraps(func)
def decorator():
print("%s called" % func.__name__)
result = func()
print("%s end" % func.__name__)
return result
return decorator
# 使用裝飾器
@logging
def test01():
return 1
# 測試用例
print(test01())
print(test01.__name__)
上面這個functools在IDE中有報錯,可能是一個外部調用方法,所以這種方法暫時不管,下次看到的時候,只要知道這是在定義一個裝飾器就可以了。
考慮到裝飾器需要適配多種參數的函數,提高復用率。
所以需要這樣定義裝飾器:
import time
def timer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print('======')
return wrapper
結合閉包的概念,梳理一下裝飾器運行的邏輯:
定義一個裝飾器名稱函數
定義一個裝飾器wrapper,可以提供多種參數的傳參
裝飾器功能,
裝飾目標函數,和wrapper相同的傳參條件
裝飾器效果(可有可無)
返回wrapper不加括號
我所理解的不加括號的意義,就是暫時不調用這個閉包內的函數,直到在外部調用時,手動添加上'()',就執行這個函數的功能。這個方法剛好和return func()相反,因為添加括號后,返回的就是一個結果值。而不添加,就是一種運算。
有參裝飾器
def auth():
def wrapper(*args,**kwargs):
name = input('username:')
password = input('password:')
if name =='scott' and password =='123':
print('auth successful')
res = func(*args,**kwargs)
return res
else:
print('auth error')
return wrapper
@auth #index = auth(index)
def index():
print('welcome to index page')
思考:
有一個運行時間計數函數裝飾器
有一個用戶登錄認證裝飾器
能否實現“當運行時間計數超過一定時間后,提示用戶登錄認證失敗”
理清裝飾器運行的包裹層關系:
@time
@auth_check
def func()
func() = time(auth_check(func))
但是如果需要校驗函數的運行時間是否超時,就應該把time放在里面
@auth_check
@time
def func()
雖然這樣看起來遞進關系對了,但是裝飾器能否向被定義的函數進行傳參,或者裝飾器之間能否進行傳參?
老師回答:裝飾器并不能向裝飾的函數進行傳參或者運算
如果需要一個對函數運行時間進行判斷并且輸出結果的裝飾器:
import time
def timer(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
res = end_time-start_time
if res<= 60:
print('函數在時限內運行完成')
else:
print('函數運行超時')
print('func run %s'%(end_time-start_time))
return wrapper
@timer