裝飾器@ 在閱讀源碼時經常遇到,所以有必要弄明白!
概括的講,裝飾器的作用就是爲已經存在的對象添加額外的功能;
本質上,裝飾器就是一個返回函數的高階函數。其接受一個函數作爲參數,並返回一個函數。 藉助Python的@語法,把裝飾器置於函數的定義處,實現功能擴展的目的;
(一)需要用裝飾器的原因
先來看一個例子:
def f0():
print('11111111')
f0()
這個函數的功能是打印出一竄字符竄。如果想要測試執行這個函數用了多長時間,我們可以這樣做:
import time
def f0():
start = time.clock()
print('11111111')
end = time.clock()
print('used:', end - start)
f0()
這樣能夠很好的達到目的。但是想測試一個模塊的所有函數的執行時間呢,就得把所有函數中都加入如上時間差的計算方法,這樣不太現實。
爲了不改變原來的函數,我們可以定義一個函數timeit,將f0()的引用傳遞給他,然後在timeit中調用f0並進行計時,這樣我們就不用修改f0函數而達到目的了:
import time
def f0():
print('11111111')
def timeit(fc):
start = time.clock()
fc()
end = time.clock()
print( 'used:', end - start)
timeit(f0)
這樣看上去邏輯沒有問題,而且可以正常的工作。但卻修改了調用部分的代碼,原本是f0()調用,現在卻成了timeit(f0),如果f0在很多處都被調用了,就需要在很多處修改代碼。注:這也是接下來的代碼裏,在timeit()函數裏需要再定義一個wrapper()函數的原因.
如果不改動調用的代碼,也就意味着調用f0()需要產生timeit(f0)的效果。我們可以這樣做,把timeit(f0)的返回值付給f0,然後直接調用f0(),就不用修改源代碼了:
import time
def f0():
print('111111111111')
def timeit(fc):
def wrapper():
start = time.clock()
fc()
end = time.clock()
print('used:', end - start)
return wrapper
f0 = timeit(f0)
f0()
這樣,我們需在定義f0以後和調用f0之前,加上f0=timeit(f0),就可以達到目的了。這就是修飾器,看起來像f0被timeit修飾了。
上面的代碼,看似沒法再精簡了,python於是提供了一個特殊的語法來降低字符輸入量:
import time
def timeit(fc):
def wrapper():
start = time.clock()
fc()
end = time.clock()
print('used:', end - start)
return wrapper
@timeit
def f0():
print('11111111')
f0()
在第12行的@timeit,效果和f0=timeit(f0)一樣,而且看上去更有修飾器的感覺。
這就是Python中修飾器的原理。
參考 https://www.tuicool.com/articles/6Z3Mbuj
(二)裝飾器使用方法
函數作爲一個對象:① 可以被賦值給其他變量,可以作爲返回值 ② 也可以被定義在另外一個函數內;
裝飾器分類:有參/無參
① 無參:用於生成一個新的裝飾器函數
② 有參:先處理這個參數,再生成一個新的裝飾器函數,然後對其做裝飾;
例
按照裝飾器與函數各自是否有參,組合共4中:
① 無參的裝飾器 ,包裝無參的函數 —— 不需要針對參數進行處理和優化
def F(fc): #裝飾器F無參
print ('2222222')
return fc
@F
def f0(): #函數無參
print('1111111')
f0()
等價於
def F(fc):
print ('2222222')
return fc
def f0():
print('1111111')
f0 = F(f0)
f0()
② 無參的裝飾器 ,包裝有參的函數
def F(fc): #裝飾器無參
def handle_args(*args, **kwargs): #處理傳入函數的參數
print("1111111")
fc(*args, **kwargs) #函數調用
print("2222222")
return handle_args
@F
def f1(a, b=2): #函數有參
print(a, b)
f1(1)
等價於
def F(fc):
def handle_args(*args, **kwargs):
print("1111111")
fc(*args, **kwargs)
print("2222222")
return handle_args
def f1(a, b=2):
print(a, b)
f1 = F(f1)
f1(1)
③ 有參的裝飾器 ,包裝無參的函數
def F(arg): #裝飾器的參數arg
print(arg)
#最終被返回的函數
def newDecorator(fc):
print(fc)
return fc
return newDecorator
@F('11111111')
def f2(): #函數無參
print('22222222')
f2()
等價於
def F(arg):
print(arg)
def newDecorator(fc):
print(fc)
return fc
return newDecorator
def f2():
print('22222222')
f2 = F('111111')(f2)
f2()
④ 有參的裝飾器 ,包裝有參的函數
def F(arg): #裝飾器參數
def handle_func(fc):
def handle_args(*args, **kwargs): #'*'返回list或tuple,'**'返回dict
print('11111111')
fc(*args, **kwargs)
print('22222222')
print(arg, fc, args, kwargs)
return handle_args
return handle_func
@F('123')
def f3(a, b=2): #函數參數
print('3333333333')
f3(1, b=3)
等價於
def F(arg):
def handle_func(fc):
def handle_args(*args, **kwargs):
print('11111111')
fc(*args, **kwargs)
print('22222222')
print(arg, fc, args, kwargs)
return handle_args
return handle_func
def f3(a, b=2):
print('3333333333')
f3 = F('123')(f3)
f3(1, b=3)
注:有3個內置的裝飾器 staticmethod, classmethod, property 。
參考:http://wklken.me/posts/2012/10/27/python-base-decorator.html