詳解閉包與裝飾器, 99%的人看了這篇文章後就懂了

我覺得在開始學一種東西時,應該用20%的時間讀取80%的基礎內容,剩下20%的內容需要用80%的時間才能深入理解,這篇文章就是讓你用20%的時間讀取80%的內容的

本文參考https://foofish.net/python-decorator.html

在python這種動態語言裏,一切都是對象,包括函數也是對象,所以便有了閉包

由於本人見識淺短,如果錯誤請各位大佬指正,非常感謝!

什麼是閉包
在這裏插入圖片描述

這個函數內含還含有一個函數,並且這個內嵌函數,使用了外部函數的參數

上面這種函數,便被稱爲閉包。跟普通函數的不同是,內嵌函數p_name用了外部函數的變量name,使外部變量生命週期被延長了。而普通函數的變量,在進入其他函數時,變量便無法再用了

這種內部函數調用外部變量的行爲,就叫做閉包。

變量生命週期被延長是因爲閉包會使用python中一個魔法屬性__closure__,它負責把外部變量生命週期延長。

你可以這樣理解以記憶,不管怎樣,name的縮進還是放在print_func裏的,所以在內部函數p_name裏能用理所當然(有些人就是這樣理解的,雖然不太對)

閉包有什麼用?閉包用處就大了,我們平常用的裝飾器,就是閉包實現的

如果,我們不是傳入一個字符串作爲參數,而是傳入函數作爲參數呢
在這裏插入圖片描述

輸出在這裏插入圖片描述, 把函數當成參數傳入,目的是爲了在此函數運行之前加點什麼, 由於傳入的函數是set_string,執行順序爲:

執行順序
在這裏插入圖片描述
print_func不重要,它只是用來傳參的,重要的是p_name,它在添加內容

如果你需要使用參數:
在這裏插入圖片描述

輸出
在這裏插入圖片描述

這個特性是由於python處處是對象,函數也可以作爲對象。比如在這個例子裏。foo跟p_name是指向同一個函數的引用。如果不理解引用,那你就把foo跟p_name當成同一個東西,沒加括號爲函數對象本身,加上括號則代表在此調用方法。調用foo即foo()則相當於p_name()。而set_string跟name又是同一個函數的引用。這也是爲什麼很多人說裝飾器是語法糖的原因。

裝飾器
其實上面傳入函數的例子,就是裝飾器了,python使用@來使用裝飾器, 此時name是一個函數對象,即set_string(‘Tory’)

在這裏插入圖片描述

你可以這麼認爲:是先執行set_string(string)的頭部部分,然後進入print_func, 到執行完p_name裏的內容,最後執行set_string(string)縮進部分的內容

如果你需要使用到函數實例裏的參數,可以使用*args與**kwargs接受函數所有參數,比如這麼寫
在這裏插入圖片描述
,輸出
在這裏插入圖片描述
如果你需要讓裝飾器應用更多的場景, 可以用三層嵌套,通過傳入參數
在這裏插入圖片描述
輸出
在這裏插入圖片描述
name依舊對應着函數對象,由於我們的裝飾器裏傳入參數,所以就以內嵌函數來對應函數對象

裝飾函數後方
如果想要裝飾函數後方,可以在最裏層,在運行函數時,在函數運行後進行代碼編寫,(最裏層可以不寫return,因爲它是直接用函數指針運行函數, 區別是前面return時不帶括號,是函數本體,最裏面一個是帶括號的,是運行實例)

裝飾器有什麼用?

具體有什麼用呢。可以用來控制執行順序,一般體現在,給函數加上什麼說明或者日誌。。

這種做法在Java中叫AOP(Aspect Oriented Programming)面向切面編程。簡單來說就像上面的。需要給方法添加一些裝飾(更正確應該說作爲一個切面插入,符合可拔插的思想)。以裝飾器爲例,有一天你想給這個某個方法前後使用日誌,因此你就使用了裝飾器。然後過了幾天,你又要使用這個函數,但這次你不用日誌了,你想給他設置訪問控制。那你這個函數的代碼完全不用動,只需再寫個訪問控制的裝飾器。然後加上就行

裝飾器極大程度重用了代碼,簡直寫代碼的好幫手

多個裝飾器的執行順序
在這裏插入圖片描述

執行順序等效於
**加粗樣式**

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章