裝飾器本質上就是一個函數,主要是爲其餘的函數添加附加的功能,裝飾器的原則有如下兩個:python
裝飾器能夠簡單的理解爲:高階函數+嵌套函數+閉包閉包
高階函數我在前面的博客中已經講過了,在這裏我再簡單的說一下吧。
高階函數:若是一個函數接收的參數是一個函數名,或者返回值是函數名,只要知足任意一個條件,這個函數就稱爲高階函數。app
def foo(): print("the result from foo") def bar(func): func() print("the result from bar") bar(foo)
在上面的例子中,我定義了兩個函數,foo()和bar(),在調用bar()函數的時候,我將foo()做爲一個參數傳給了bar(),運行就會獲得foo()的結果,這樣的函數就能夠成爲高階函數。函數
def foo(): print("the result from foo") def bar(func): return func bar(foo)
調用bar()函數 返回的就是foo()的內存地址,這也能夠稱爲高階函數。code
函數嵌套:函數嵌套我在前面也已經講過了,其實就是函數的內部再聲明函數,很好理解的一個概念,舉個例子:以下內存
def foo(): print("the result from foo") def bar(): print("the result from bar") bar() foo()
在上面這個例子中,我在函數foo()的內部又寫了一個bar()函數,並在下面調用了該函數,這種方式就叫作函數嵌套,能夠嵌套不少層,只要注意函數縮進問題作用域
在Python中閉包的表現形式能夠理解爲:若是在一個內部函數裏,對在外部做用域(但不是在全局做用域)的變量進行引用,那麼內部函數就被認爲是閉包(closure)。
單從上面的定義可能很難理解,我下面用一個簡單的程序說明一下:博客
def foo(): a=1 b=2 def bar(): c=3 return a+b+c return bar print(foo()())
在上面這個例子中,bar()就是foo()的一個內部函數,在bar()的局部做用於能夠直接使用foo()的局部變量a,b,簡單的說,這種內部函數能夠訪問外部函數變量的行爲,就叫作閉包。class
爲了能讓你們更好的理解裝飾器,我會分步作出這個實例。test
先定義一個函數name(),3秒後打印名字
import time def name(): time.sleep(2) print("my name is 尼古拉斯趙四") name()
如今有一個需求,我想要統計這個函數一個運行了多少秒,在不修改源代碼的狀況下,就須要給這個函數寫一個裝飾器來完成這個需求。
def timmer(func): #定義一個形參,就是爲了接受name()這個函數,注意前面文章就已經強調過的:函數即變量---func=name def wapper(): #定義函數wapper(),用來接收name的參數 start_time=time.time() func() #實質上就是在運行test() stop_time=time.time() print("name()函數一共運行了%s 秒"%(stop_time-start_time)) return wapper import time @timmer #使用裝飾器的方法,經過@+做爲裝飾器的那個函數名 def name(): time.sleep(2) print("my name is 尼古拉斯趙四") name() 運行結果: my name is 尼古拉斯趙四 name()函數一共運行了2.0002381801605225 秒
上面這個例子就是一個簡單的裝飾器,沒有修改原函數的調用方法和返回值,裝飾器timmer中用到了高階函數+函數嵌套+閉包的知識,ok,完美。
def name(my_name,my_age): time.sleep(2) print("my name is %s,my age is %s"%(my_name,my_age)) name("尼古拉斯趙四",18)
我須要隨機傳入兩個值,打印出他的姓名和年齡,若是原函數這樣調用,使用上面的裝飾器確定會出錯,那就須要在裝飾器函數中作以下修改:def wapper(my_name,my_age)
和func(my_name,my_age)
這兩行加入相同的參數,也是能夠的,但若是name函數我再修改呢,不傳入兩個參數了 ,傳3個 或更多,每次都要去修改豈不是很麻煩,這就須要作一些改變了 。
def timmer(func): def wapper(*args,**kwargs): #這裏用*args,**kwargs代替,這樣 ,無論原函數傳入多少個參數,均可以匹配 start_time=time.time() func(*args,**kwargs) #一樣,接受任意多個參數 (若是不懂這個什麼意思,翻看我前面函數篇的博客,有講到) stop_time=time.time() print("name()函數一共運行了%s 秒"%(stop_time-start_time)) return wapper
def name(my_name,my_age): time.sleep(2) print("my name is %s,my age is %s"%(my_name,my_age)) return "尼古拉斯 你真年輕" print(name("尼古拉斯趙四",18)) 運行結果: my name is 尼古拉斯趙四,my age is 18 尼古拉斯 你真年輕
在這個函數中,我需求是在運行完函數返回"尼古拉斯 你真年輕"這句話,仍是用上面的裝飾器返回值會是my name is 尼古拉斯趙四,my age is 18 name()函數一共運行了2.000795364379883 秒 None
,返回值是None而不是想要的結果,你們能夠試一下,因此要加以下修改:
def timmer(func): def wapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) #將func()運行結果賦值給變量res stop_time=time.time() print("name()函數一共運行了%s 秒"%(stop_time-start_time)) return res #返回res,其實就是在返回name() return wapper
上面就詳細寫了裝飾器的實現過程,就先寫到這,後續我會豐富裝飾器並作出一個通俗易懂的例子供你們參考,稍後一些時間也會發布在個人博客裏,感興趣的到時候能夠看一下,但願能夠幫助你們對裝飾器有更好的理解。