1. 程式人生 > >關於裝飾器,看了幾篇文章,總結下

關於裝飾器,看了幾篇文章,總結下

裝飾器是為了動態為函式增加功能,而不改變函式的程式碼,而存在的。編寫一個高階函式,讓他接收另一個函式,在內部對其包裝,是為裝飾器。

裝飾器的本質就是一個函式,用來修飾你要裝飾的函式,別想複雜了。

呼叫裝飾器,下面是要修飾的函式,則下面的函式就整體看做一個變數,傳給了裝飾器函式,傳過去之後,當時就返回了裝飾器函式的內部函式,即此時:傳入函式=裝飾器內部函式。
在這裡插入圖片描述
如圖:f=foo()=bar, f()=bar()
這裡要注意一點,就是裝飾器函式在裝飾了被裝飾函式之後,其外層程式碼會立刻執行,被裝飾函式立馬等於內部函式,然後被裝飾函式呼叫時,內部函式才被呼叫。

再看多層裝飾器執行順序:

在這裡插入圖片描述
在這裡插入圖片描述
為什麼是先執行 inner_b 再執行 inner_a 呢?為了徹底看清上面的問題,得先分清兩個概念:函式和函式呼叫。上面的例子中 f 稱之為函式, f(1) 稱之為函式呼叫,後者是對前者傳入引數進行求值的結果。在Python中函式也是一個物件,所以 f 是指代一個函式物件,它的值是函式本身, f(1) 是對函式的呼叫,它的值是呼叫的結果,這裡的定義下 f(1) 的值2。同樣地,拿上面的 decorator_a 函式來說,它返回的是個函式物件 inner_a ,這個函式物件是它內部定義的。在 inner_a 裡呼叫了函式 func ,將 func 的呼叫結果作為值返回。

裝飾器函式在被裝飾函式定義好後立即執行


其次得理清的一個問題是,當裝飾器裝飾一個函式時,究竟發生了什麼。現在簡化我們的例子,假設是下面這樣的:
在這裡插入圖片描述
所以,當直譯器執行這段程式碼時, decorator_a 已經呼叫了,它以函式 f 作為引數, 返回它內部生成的一個函式,所以此後 f 指代的是 decorater_a 裡面返回的 inner_a 。所以當以後呼叫 f 時,實際上相當於呼叫 inner_a ,傳給 f 的引數會傳給 inner_a , 在呼叫 inner_a 時會把接收到的引數傳給 inner_a 裡的 func 即 f ,最後返回的是 f 呼叫的值,所以在最外面看起來就像直接再呼叫 f 一樣。
當理清上面兩方面概念時,就可以清楚地看清最原始的例子中發生了什麼。

當直譯器執行下面這段程式碼時,實際上按照從下到上的順序已經依次呼叫了 decorator_a 和 decorator_b ,這是會輸出對應的 Get in decorator_a 和 Get in decorator_b 。 這時候 f 已經相當於 decorator_b 裡的 inner_b 。但因為 f 並沒有被呼叫,所以 inner_b 並沒有呼叫,依次類推 inner_b 內部的 inner_a 也沒有呼叫,所以 Get in inner_a 和 Get in inner_b 也不會被輸出。

然後最後一行當我們對 f 傳入引數1進行呼叫時, inner_b 被呼叫了,它會先列印 Get in inner_b ,然後在 inner_b 內部呼叫了 inner_a 所以會再列印 Get in inner_a, 然後再 inner_a 內部呼叫的原來的 f, 並且將結果作為最終的返回。這時候你該知道為什麼輸出結果會是那樣,以及對裝飾器執行順序實際發生了什麼有一定了解了吧。

自我總結:多層裝飾器時,定義函式時先執行內部decorator再執行外部decorator,最後落到外部decorator的內層函式上(wapper或inner),呼叫函式時(f()),先執行外部decorator的內層函式,然後逐步向裡執行,直到最終執行被裝飾函式(核心),結束。
定義函式後:由內到外(被裝飾函式定義好了就會立即執行)
呼叫函式後:由外到內(呼叫時接著上面定義好的結果繼續執行)

歡迎討論