《python核心編程》這本書一看就知道是搞技術的人寫的,和我以前看qt的某些書籍一樣。東一榔頭西一棒子。不過還好,python的大部分我都知道了,看這本書不過是在查漏補缺,而且本身也有C/C++的基礎。如果是剛入門的同學,如果看這本書能看懂,那真是天才了。
不過如果你同我一樣,抱著查漏補缺的態度,那麼這本書會有很多閃光點。其中一個就是這個python裝飾器。
複合函數
學過數學的人,應該都知道複合函數或者運算元吧:z=h。g。f (h,g是運算元,f是g運算元的定義域), 圈是複合符號.這樣就形成了一個複合函數鏈。前面運算元的範圍是後面運算元的定義域。
如果我們把python中的普通函數看作f,那麼裝飾器就是後面的g,h 運算元 ,返回的z是與f具有接受相同參數能力的新函數f_new,那麼我門在使用的時候直接輸入f(),實際上就是使用的f_new()。
無參數裝飾器
- 現在簡單來看下f_new=g。f的形式,也就是不帶參數的裝飾器。顯然g的定義域是python的普通函數f,傳回值是f_new。f_new與f具有接受相同參數的能力。
下面看看代碼說明:
def foo(): print 'in foo()'def wrap(func): print 'in wrap' return func
這個foo()函數是普通的python函數,它可以作為運算元g的輸入;下面的wrap定義域是func函數,返回的也是一個與輸入func同類型的函數,顯然它就是運算元g
@wrapdef foo(): print 'in foo()'foo()
現在有 foo=f_new=wrap 。 foo ; 現在再調用foo(),實際上就是調用的f_new()也就是 (wrap。foo) ()
帶參數裝飾器
- 現在來看代參數的裝飾器,其數學運算式為:f_new=g(x)。f ;同樣,f是我們的普通的python函數。不同的是先用運算元g,產生一個新的運算元g(x),g(x)的定義域才是f,返回的函數為與f同類型的f_new。
下面是代碼說明:
def wrap_out(arg): print 'in wrap_out,and arg is %s'%arg def wrap_inner(func): print 'in wrap_inner' return func return wrap_inner@wrap_out('test1')def foo() print 'in foo'foo()
現在就有foo=f_new=wrap_out(arg)。foo
寫成方程組:
wrap_inner=wrap_out(arg)
foo=wrap_inner(foo)
有了上面的理解後,就應該非常清楚,封裝函數內部應該是什麼結構,應該返回什麼。只要把函數的複合鏈寫出來,就很容易搞清楚裝飾器的結構。
比如上面的帶參數的裝飾器,我看過很多同學寫的解釋,反正就是很複雜。但是如果把foo=wrap_out(arg)。foo 寫出來分析下。wrap_out帶參數arg,而且它返回的值必須能接受foo函數,也就是wrap_inner=wrap_out(arg),wrap_inner必須能接受foo作為輸入。再著,wrap_inner(foo)返回的值必須是一個和原foo接受統一的參數的函數,。那整個結構就很清楚了。
很多裝飾器的複合
現在來看一個比較複雜的,雙重的裝飾器。
def wrap_out2(arg): print('in wrap_out 2 ,and arg is %s'%arg) def wrap_inner2(func): print('in wrap_inner 2') func('in wrap 2 call func') def fun_new2(): print('in func_new 22') return fun_new2 return wrap_inner2def wrap_out(arg): print('in wrap_out,and arg is %s'%arg) def wrap_inner(func): print('in wrap_inner') func() def func_new(h): print(h) return func_new return wrap_inner#foo_new=wrap_out2('world') o wrap_out('hello') o foo@wrap_out2('world')@wrap_out('hello')def foo(): print('in foo')foo()
輸出結果為:
in wrap_out 2 ,and arg is world
in wrap_out,and arg is hello
in wrap_inner
in foo
in wrap_inner 2
in wrap 2 call func
in func_new 22
數學式子為:
foo=wrap_out2('world')。wrap_out('hello')。foo
通過輸出結果可以判斷其化解順序為:
foo=wrap_inner2。wrap_out('hello')。foo
foo=wrap_inner2。wrap_inner。foo
也就是說運算元化簡是從左至右,而普通計算是從右至左。要是這樣都不能理解裝飾器,那就只有去撞牆了。^_^
還可以看到wrap_out 和wrap_out2的輸入與輸出是不同的,在數學上輸入與輸出相同稱為變換,如果不同那麼只能稱為映射了。映射不能隨意的匹配,在這裡只能寫成wrap_out2。wrap_out的形式,而不能顛倒它們的複合順序。當然一般的裝飾器,都是經過一定處理後,返回原函數,也就是變換。
綜上:func=h([arg])。g([arg])。func =T 。func ;T就是一個運算元,它的定義域和範圍都是接受相同參數的 func 。老func 輸入 T 返回同類型的 新func 。也就是說裝飾器就是運算元T,整個運算元T是func空間的變換;over