python重写和装饰器_python装饰器
python裝飾器的本質,就是閉包!
我們一般談Python的閉包,都是指普通的入參,而談裝飾器的時候,入參一定有函數!閉包和裝飾器,返回的都是函數。函數是代碼的最小封裝單位,裝飾器作用于函數,它不影響函數自身的執(zhí)行,只是在函數的執(zhí)行前后增加一些“裝飾性”的動作。裝飾器被稱為python的語法糖(syntax sugar),也被視為python支持AOP編程(面向切面編程)的工具。
簡單裝飾器
以下代碼實現了一個最簡單的額裝飾器,功能是給帶上裝飾器的函數,在函數執(zhí)行前,增加一行print打印,代碼如下:
test1函數加上了calling_trace裝飾器,在第7行,這一行代碼的作用,相同于第11行的注釋。test1還是test1,這個名字沒有變,只是換成了一個閉包函數的返回值,此閉包函數,就是裝飾器,它接收一個函數作為參數。以上代碼運行效果如下:
$ python3 deco.py
calling test1
test1 is runing...
我們還可以稍微復雜一點點,既然是裝飾器,被裝飾的函數執(zhí)行前后都可以加點小動作。修改后的代碼如下:
def calling_trace(func):
def wrapper():
print('calling', func.__name__)
a = func()
print('this is the reture:',a)
return wrapper
@calling_trace
def test2():
print('test2 is runing...')
return 'www.pynote.net'
# test2 = calling_trace(test2)
test2()
新的裝飾器沒有改名字,只是獲取了被裝飾函數的返回值,并將返回值打印出來。運行效果如下:
$ python3 deco.py
calling test2
test2 is runing...
this is the reture: www.pynote.net
test2函數執(zhí)行前后,都增加了一些動作,作為裝飾!
這就是裝飾器的概念:不修改已有函數的代碼,也不修改已有函數的調用處代碼,卻達到了豐富函數功能的效果!本質上裝飾器是對函數的一種封裝,只是我們要理解@語法。
閉包和裝飾器
裝飾器的本質就是閉包,我們如果把上面這段代碼重寫一遍,采用閉包的語法形式,不使用@語法,效果是完全一樣的:
def calling_trace(func):
def wrapper():
print('calling', func.__name__)
a = func()
print('this is the reture:',a)
return wrapper
def test2():
print('test2 is runing...')
return 'www.pynote.net'
t = calling_trace(test2)
t()
以上代碼,沒有@語法的使用啦,用變量t來獲取calling_trace返回的函數對象,然后調用,效果與使用裝飾器完全一樣:
$ python3 deco.py
calling test2
test2 is runing...
this is the reture: www.pynote.net
使用@語法,閱讀代碼就如吃糖!
裝飾帶參數的函數
如果被裝飾的函數有參數,需要在裝飾器內部原樣復制函數的參數定義。請看示例:
def calling_trace(func):
def wrapper(a,b,c=3):
print('calling', func.__name__)
a = func(a,b,c)
print('reture value:',a)
return wrapper
@calling_trace
def test3(a,b,c=3):
print('test3 is runing...')
return a+b+c
test3(1,2,5)
test3(1,2)
裝飾器返回的函數的參數定義,要與被裝飾函數的參數定義保持一致!以上代碼運行結果如下:
$ python3 deco.py
calling test3
test3 is runing...
reture value: 8
calling test3
test3 is runing...
reture value: 6
就算裝飾參數個數不定的函數,語法上也是一樣的,請看下面代碼,test4函數的參數個數不定:
def calling_trace(func):
def wrapper(*args):
print('calling', func.__name__)
a = func(*args)
print('reture value:',a)
return wrapper
@calling_trace
def test4(*args):
print('test4 is runing...')
return sum(args)
test4(1,2,3,4,5,6,7,8)
test4(23,34,45,56)
*args表示一個tuple,在函數定義處出現,就是packing打包調用時的參數,在調用時出現,就是unpacking展開tuple。跟**kw(對應dict)用法一樣。以上代碼運行效果:
$ python3 deco.py
calling test4
test4 is runing...
reture value: 36
calling test4
test4 is runing...
reture value: 158
給帶參數的函數加裝飾器,還有一種更通用更常見的參數寫法,這種寫法在修改函數參數和調用處時,有可以反過來保持裝飾器部分代碼不變。示例如下:
def calling_trace(func):
def wrapper(*args, **kw):
print('calling', func.__name__)
a = func(*args, **kw)
print('reture value:',a)
return wrapper
@calling_trace
def test5(a,b,c=3):
print('test5 is runing...')
return a+b+c
test5(1,2)
test5(1,2,c=8)
裝飾器中使用*args和**kw來接收和傳遞參數!這個地方要好好體會,這是python的一個特別精妙的地方。以上代碼運行結果如下:
$ python3 deco.py
calling test5
test5 is runing...
reture value: 6
calling test5
test5 is runing...
reture value: 11
帶參數的裝飾器
本文以上示例,都是不帶參數的裝飾器,在使用@語法的時候,沒有參數。而裝飾器本身也可以帶參數,通過在裝飾器外再使用閉包,給裝飾器封裝其執(zhí)行環(huán)境,可以使裝飾器的功能更強大更靈活,也可以更好的控制函數的執(zhí)行(比如在某些情況下不執(zhí)行)。而帶參數的裝飾器,在語法上,也就跟閉包區(qū)分開來。(應該就是這個原因,閉包和裝飾器在python中是分成了兩個概念)
def calling_trace(run):
def deco(func):
def wrapper(*args, **kw):
print('calling', func.__name__)
if run == 1:
a = func(*args, **kw)
print('reture value:',a)
else:
print('not allow to run')
return wrapper
return deco
# test5 = calling_trace(run=1)(test5)
@calling_trace(run=1)
def test5(a,b,c=3):
print('test5 is runing...')
return a+b+c
@calling_trace(run=0)
def test6(*args):
print('test6 is runing...')
return sum(args)
test5(1,2)
test5(1,2,c=8)
test6(23,34,45,56)
先看一下這段代碼的運行結果:
$ python3 deco.py
calling test5
test5 is runing...
reture value: 6
calling test5
test5 is runing...
reture value: 11
calling test6
not allow to run
達到預期,test5可以執(zhí)行,而test6不允許執(zhí)行。
calling_trace實際上就是一個閉包,它有一個參數,run,調用calling_trace(run=1)后,就相當于返回了一個有run這個參數的裝飾器。然后再用這個裝飾器去“修飾”它自己的參數func。這個效果,就如注釋掉的那行代碼。如果不使用@語法,把那行代碼注釋打開(放在test5的定義后面),運行效果一模一樣!
多重裝飾器
函數可以有多個裝飾器!多個裝飾器的效果,就相當于對函數進行了多層的封裝包裹,而不同的裝飾器對函數執(zhí)行的功能影響,完全獨立。比如有個裝飾器用來控制函數是否能夠被執(zhí)行,另一個裝飾器控制函數的所有raise出來的異常。
@a
@b
@c
def tt(): pass
# tt = a(b(c(tt)))
tt函數上面帶3個裝飾器,想過正如注釋掉的那行代碼。
我真的覺得python裝飾器的設計,實在是非常精妙!
-- EOF --
總結
以上是生活随笔為你收集整理的python重写和装饰器_python装饰器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jwt token 太长_理解 JWT
- 下一篇: java 栈 大小_java – J