有點類似函式的目的(函式的目的就是當你有1w個**塊要新增相同的功能時,只需要呼叫已經寫過一次的函式就好,而不是要寫1w次同樣的**),
當你有1w個函式要新增相同的功能時,只需要呼叫已經寫過一次的修飾器就好;還有就是當你的程式很龐大時,想要給你的程式中的某個模組加上新的功能,不可能說直接修改源**, 因為說不定修改的部分後面會引用到,擅自修改就可能會報錯
裝飾器的原則:
1.不修改被修飾函式的源**
2.不修改被修飾函式的呼叫方式
裝飾器 = 高階函式+函式巢狀+閉包
高階函式:
1.函式接收的
引數是乙個函式名
2.函式的
返回值是乙個函式名
3.只要滿足上述條件的其中乙個都可以稱之為高階函式
例如:
def a():
print('from a')
def b(func): #這個b函式滿足第乙個條件
print(func)
def c(): #這個c函式滿足第二個條件
return func
1.假定這裡我們先用高階函式給原函式寫乙個統計時間的方法,有:
def a(): #要被統計的函式
x = sum([i for i in range(1000000)])
print(x)
def time_test(func):
import time
start_time = time.time()
func()
end_time = time.time()
result = end_time - start_time
return result
res = time_test(a)
print(res)
在此,雖然我們寫好了統計時間的函式,但是因為這樣做
修改了原來函式a()的呼叫方法,所以這個肯定算不上是乙個裝飾器
我們用高階函式的第乙個條件來完成了在不修改原函式**的情況下對函式增加了額外的功能,但是這裡改變了呼叫方法
2.接著讓我們來看高階函式的第二個用法,用這個可以對函式進行一些巧妙的處理
def a():
print('from a')
def b(func):
print('from b')
# 一堆**
return func
a = b(a)
a() #呼叫a
作為結果而言,a = b(a)這個式子可以認為是a = a,只不過這裡的a是作為b函式的返回值存進去的(既然是返回值,那麼說明b函式已經執行了一次)
在此,a這個變數原來存的是a的位址,我們把函式b的返回值賦給a以後(在這裡函式b都執行了一次),a的值就不再是函式a的位址了,而是b(a)了,
也就意味著每當執行a函式的時候,都會先把b函式執行一遍。
這樣做的目的在於,
我們在不改變原有呼叫方式的情況下(函式a的呼叫方法還是a()),執行了乙個額外的函式。如果我們在這個額外的函式中,寫上我們要附加的功能(就是上面的第一點),這樣是不是就滿足了我們裝飾器的要求:在不修改原函式和其呼叫方式的基礎上給其增加新的功能
3.接下來,我們就把條件一(不修改源**的條件下附加新的功能)和條件二(不修改呼叫方式的情況下執行乙個額外的函式)結合起來:
def a(): #要被統計的函式
x = sum([i for i in range(1000000)])
print(x)
def time_test(func):
import time
start_time = time.time()
func()
end_time = time.time()
result = end_time - start_time
print('執行的時間為',result)
return func
a = time_test(a)
a()
函式執行的結果為:
我們會發現,截圖裡多了乙個求和結果,也就是說多執行了一次a。
原因在於,在額外的函式test_time(或者說是b)中,
多了乙個原本沒有的'func()',這個就導致了a函式多執行了一次。
在 a = time_test(a) 的時候,我們是要將time_test(a)賦給a這個變數以達成'執行額外的函式'這個目的的,但是現在我們因為在這個過程中函式b執行了一次,所以原來沒有的func()讓a函式多跑了一次
不過即使如此,我們還是離裝飾器很近了,只要我們把這個多出來的執行給去掉,就可以說基本是大功告成了
4.函式巢狀和閉包:
再運用函式巢狀的知識,對上面的例子進行一些修改,就可以得到以下:
def time_test(func):
import time
start_time = time.time()
func()
end_time = time.time()
result = end_time - start_time
print('執行的時間為', result)
#@time_test
def a(): # 要被統計的函式
x = sum([i for i in range(1000000)])
print(x)
a = time_test(a)
a()
也就是說,我們在執行a = time_test(a)的時候,
因為現在的這個time_test函式沒有內容(內容都挪到子函式裡去了),所以多出來的func()自然就沒有了,也就是說a = test_time(a)裡實際上存的是test_time()的子函式test_time2的位址【可能有人會問這不是子函式嗎,他不是只在父函式裡生效的嗎,因為這個子函式已經作為返回值輸出了,所以他可以被外界引用】。
另外呢,可以把a = time_test(a)這一步用@time_test這個python函式的語法糖來代替。這個「@+函式名」要放在被裝飾的函式前面,這裡的@time_test就相當於a = time_test(a),他們是一樣的。
5.在簡單的函式裝飾器完成之後
def time_test(func):
import time
start_time = time.time()
jieguo = func()
end_time = time.time()
result = end_time - start_time
print('執行的時間為', result)
return jieguo
b.緊接著,我們還要進行最後的新增,因為裝飾器是裝飾不同函式的,所以當我們的被裝飾函式有傳入的值的時候,因為我們現在寫的修飾器函式沒有形參,所以會報錯
所以我們要給裝飾器加上形參以便接收不同函式的不同的形參:
def time_test(func):這樣,最終我們就完成了乙個簡單的裝飾器def time_test2(*args,**kwargs):
import time
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
result = end_time - start_time
print('執行的時間為', result)
return time_test2
Python 裝飾器總結
目錄閉包 裝飾器簡單裝飾器 修飾帶參函式的裝飾器 本身帶引數的裝飾器 類裝飾器 裝飾器缺點 裝飾器用途 說到裝飾器就不能忽略閉包,下面先介紹一下閉包的概念 在一些語言中,在函式中可以 巢狀 定義另乙個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。閉包可以用來在乙個函式與一組 私有 變...
Python 裝飾器總結
目錄 閉包裝飾器 簡單裝飾器 修飾帶參函式的裝飾器 本身帶引數的裝飾器 類裝飾器 裝飾器缺點 裝飾器用途 說到裝飾器就不能忽略閉包,下面先介紹一下閉包的概念 在一些語言中,在函式中可以 巢狀 定義另乙個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。閉包可以用來在乙個函式與一組 私有 ...
python學習 裝飾器
def w1 func def inner print 正在驗證 func 閉包 return inner def f1 print f1 def f2 print f2 f1 w1 f1 呼叫的 f1 發生改變 f1 在沒有修改 f1 引用的前提下,完成對 f1 的擴充套件 執行結果 在很多情況下...