裝飾器引入
有函數(shù)如下:(用來讀取文件內(nèi)容并顯示)
def readFile(file):
f = open(file, 'r', encoding="utf-8")
for line in f.readlines():
print(line)
f.close()
是(sei)讀取文件哪有這樣寫的,打開前不檢查文件是否存在,而且讀文件操作也可能會出錯。一般情況下我們會推薦with open(path, 'r', encoding='utf-8') as f: 但是今天我們我們故意不用改方法,我們可以封裝一下函數(shù)如下:
import os
def readFileRight(file):
if not os.path.exists(file):
print("文件不存在!---zz")
else:
try:
readFile(file)
except:
print('讀取出錯啦?。?)
現(xiàn)在又有個糊涂蛋定義了writeFile(path)也是沒有做安全判斷,我們可以同樣使用上述方式繼續(xù)封裝函數(shù)。函數(shù)少我們可以這樣做,但試想下若有幾十個類似這樣的函數(shù),每一個函數(shù)都封裝一遍,en 。。敗類。 完全沒有我們程序猿的樣子(懶)。此時python的裝飾器就可以閃亮登場了。
裝飾器可以將函數(shù)a嵌入到函數(shù)b中
裝飾器也可以進一步封裝函數(shù),但是不像我們平常封裝函數(shù)一樣,同樣的功能需要對每個原函數(shù)都封裝一遍。麻煩。感受下裝飾器:
import os
def readFileRight(func):
def wrapper(file):
if not os.path.exists(file):
print("文件不存在!---zz")
else:
try:
readFile(file)
except:
print('讀取出錯啦??!')
return wrapper
@readFileRight
def readFile(file):
f = open(file, 'r', encoding="utf-8")
for line in f.readlines():
print(line)
f.close()
file = 'hhh.txt'
readFile(file)
此時只需要定義一遍readFileRight() ,在后續(xù)的需要做安全檢查的函數(shù)前使用@readFileRight裝飾一下,現(xiàn)在調(diào)用函數(shù)的方式不變但函數(shù)'readFile()'已經(jīng)具備了安全檢查的功能。
即當我們有如下需求時可以使用裝飾器,也推薦使用裝飾器:
@1.使用別人的代碼時,某個功能不能滿足自己的需求,盡量別動別人的源碼
@2.相同的功能在大量的函數(shù)前需要被添加
裝飾器由簡到難
注:hh()為原始函數(shù),log()為裝飾器函數(shù)
裝飾 無參函數(shù):
hh() = log(hh)()
def log(func):
def wrapper():
print("start -------")
func()
print("end ------")
return wrapper
@log
def hh():
print("hhhhhhhhhhh")
hh()
輸出:
start -------
hhhhhhhhhhh
end ------
使用@log裝飾函數(shù)hh 其實是執(zhí)行了 hh = log(hh) 得到 hh = wrapper
即改變了hh變量所指向的函數(shù)。而原來的函數(shù)當做參數(shù)傳到了wrapper中,所以當我們執(zhí)行hh()時實際上是執(zhí)行了wrapper()。此處在函數(shù)中又定義了函數(shù),并返回函數(shù),需要我們知道2個知識點 python 的閉包 以及 python中一切皆對象,函數(shù)也是對象
裝飾 有參函數(shù):
hh(*args) = log(hh)(*args)
def log(func):
def wrapper(*args,**kwargs):
print("start -------")
func(*args, **kwargs)
print("end ------")
return wrapper
@log
def hh(info):
print(info)
hh(r"i'm ok")
輸出:
start -------
i'm ok
end ------
使用@log裝飾函數(shù)hh(info) 其實是執(zhí)行了 hh = log(hh) 得到 hh(info) = wrapper(info) ,所以當我們執(zhí)行hh(info)時實際上是執(zhí)行了wrapper(info)。此處函數(shù)wrapper(*args, **kwargs)表示可以接受任意參數(shù),一般情況下該模式都可以,但是在一些特殊情況下:在原函數(shù)外部需要使用函數(shù)的參數(shù)時,會需要明確指明函數(shù)的傳參情況,后面我們講裝飾類函數(shù)時會遇到
帶參裝飾器:
hh() = log(text)(hh)()
import time
def log(text):
def decorator(func):
def wrapper():
print('開始時間---%s' %text )
print("start -------")
func()
print("end ------")
return wrapper
return decorator
text = time.time()
@log(text)
def hh():
print('hhhhhhhh')
輸出:
開始時間---1533918264.889485
start -------
i'm ok
end ------
使用@log(text)裝飾函數(shù)hh() 其實是執(zhí)行了 hh = log(text)(hh) 得到 hh = decorator(hh) ,進一步裝飾得到hh = wrapper 所以當我們執(zhí)行hh()時實際上是執(zhí)行了wrapper()。而此時原函數(shù)hh()以及參數(shù)text也通過裝飾的過程將內(nèi)容傳入了wrapper()中。該裝飾模式可以使用在程序運行的過程中,我們可以動態(tài)的傳入需要的參數(shù)。自己探索:hh(info) = log(text)(hh)(info)
總結(jié)
記住4中裝飾模式,了解本質(zhì)既就算再復雜我們也可以一點點拆解
@1.裝飾 無參函數(shù):hh() = log(hh)()
@2.裝飾 有參函數(shù):hh(*args) = log(hh)(*args)
@3.帶參裝飾器裝飾無參函數(shù): hh() = log(text)(hh)()
@4.帶參裝飾器裝飾有參函數(shù): hh(*args) = log(text)(hh)(*args)
提前預告
下一篇:Python 裝飾器 續(xù)集
會更加深入的分析裝飾器的原理。即擴展裝飾器的用途。期待吧!!
注:
該文章中的很多名詞都是我自己的叫法。比如:裝飾無參函數(shù),這些都是為了利于分析講解,正規(guī)叫法我也不太清除,還有本人也是python小白一枚,如果上面的分析有誤,歡迎大家指正,一起學習,謝謝~~