ython有著強大的表示式語法和函式特性,其中乙個我的最愛便是裝飾器。 在設計模式中,裝飾器能夠在不使用子類的情況下動態的修改函式、方法或類的功能。
當你需要擴充套件某個函式的功能卻不想直接修改這個函式的時候,裝飾器就可以派上用場了。 實現裝飾器模式有很多種方法,但是python通過強大的語法支援來讓這個變得相當容易。
在這篇文章中我將深入講解python的函式裝飾器,並通過一系列的原始碼示例來徹底講清楚這個東西。 所有例子都在python2.7下執行通過,不過只需要稍作改變就可以執行在python3上了, 甚至我猜測什麼都不用改變都可以的,讀者可以自己去試試。
本質上來講,裝飾器是以包裝器形式工作的,其實就是在執行目標函式之前或之後加入自己的邏輯, 而不需要改變目標函式本身就可以增強它的功能,也就是說裝飾了它。
你需要知道的函式
在深入討論之前,有一些基本的概念需要講明清楚。 在python中,函式是一等公民,它們就是物件,因此我們可以使用它來做很多事。
1.把函式賦值給某個變數:
2.在某個函式內部定義另外乙個函式:
3.函式可以被當做引數傳遞給另外乙個函式:
4.函式返回值可以是其他函式:
5.內部函式可以訪問包含它的函式的區域性變數:
其實就是我們所說的閉包,在構建裝飾器的時候這是乙個非常有用的模式。另外還要注意,python只允許讀取外部變數而不允許修改。
觀察一下下面的**,注意我們是如何通過修改上面例項**來讀取外部函式中的name引數值並返回乙個新的函式的。
python中的閉包
既然講到這裡了,就不得不去提一下python中的閉包了。
定義:如果在乙個內部函式裡,對在外部作用域(外部函式的區域性變數或者引數,但不是在全域性作用域)的變數進行引用,那麼內部函式就被認為是閉包(closure)
分解來說,包含下面3個條件:
需要函式巢狀, 就是乙個函式裡面再寫乙個函式.
外部函式需要返回乙個內部函式的引用
外部函式中有一些區域性變數或者引數, 並且, 這些區域性變數或引數在內部函式中有使用
一些概念:
自由變數: 外部函式中定義的區域性變數, 並且在內部函式中被使用
閉包: 那個使用了自由變數並被返回的內部函式就稱為閉包
支援閉包的語言有這樣的特性:
函式是一階值(first-class value),即函式可以作為另乙個函式的返回值或引數,還可以作為乙個變數的值
函式可以巢狀定義,即在乙個函式內部可以定義另乙個函式
**示例:
構造裝飾器
函式裝飾器就是已存在函式的乙個包裝器。我們把上面的這些結合起來就能構建乙個裝飾器了。
下面例子中我們先構造乙個函式來用p標籤包裝其他函式返回的乙個字串。
這是我們的第乙個裝飾器——乙個增強其他函式功能並返回新函式的函式。 為了讓get_text函式被p_decorate裝飾,我們只需要將get_text作為引數傳給後者, 並將結果賦值給乙個變數,然後就可以對這個變數函式呼叫就能實現效果了。
主要原來的函式有乙個name引數,那麼我們呼叫的時候將這個引數傳遞給裝飾器函式就行了。
python的裝飾器語法
python通過一些語法糖讓建立和使用裝飾器變得相當簡單。 我們並不需要使用語句get_text = p_decorator(get_text)來裝飾get_text。 有乙個快捷方式可以做到,它會在被裝飾函式前面加一層裝飾函式。裝飾器的名字需要使用@字首。
現在我們再考慮下利用2個其他的函式來裝飾我們的get_text函式,在其輸出結果上新增乙個div和strong標籤。
如果我們使用原來的語法,那麼就得這麼寫:
但是在python中,你就可以這樣來定義了:
上面需要注意的是裝飾器的順序,如果順序不同,輸出結果也會不一樣。
裝飾方法
在python中,其實方法就是第乙個引數為當前物件的引用的函式而已。 我們同樣能夠給方法構造裝飾器,只需要將self引數放到包裝函式中。
乙個更好的做法是改造我們的裝飾器使他們可以作用於函式以及類方法。 可以將*args和**kwargs作為包裝器的引數,然後它就能接受任意數量的位置引數和關鍵字引數了。
給裝飾器傳遞引數
回顧下上面的例子,你會發現例子中的裝飾器太過冗餘了。 3個裝飾器(div_decorate,p_decorate, strong_decorate)擁有相同功能,只是使用了不同的標籤包裝而已。
我們可以做得更好,為什麼不使用一種更加通用的方式,將標籤作為引數傳遞進來呢?
除錯被裝飾函式
最後當我們除錯被裝飾函式時會發現它的名字、模組和文件字串都發生了改變。
我們期望的輸出應該是get_text,get_text的name、doc 和 module已經被包裝函式覆蓋了。
使用functools來解決
幸運的是python2.5版本以上有了乙個functools包可以來解決這個問題。 只需要簡單在包裝函式上標註@wrap標籤即可。
從結果可以看出get_text函式的屬性都恢復正常了。
**使用裝飾器
這篇文章中的例子相對來講是比較簡單的。它能給你的程式帶來很大的方便。 一般來講,裝飾器用在需要擴充套件某個函式行為而又不想改變這個函式本身內容的時候。
我建議你查閱一下python decorator庫來獲取更多非常有用的裝飾器。
下面是乙個值得去檢視的關於裝飾器的其他資源列表:
python裝飾器 Python 裝飾器
簡言之,python裝飾器就是用於拓展原來函式功能的一種函式,這個函式的特殊之處在於它的返回值也是乙個函式,使用python裝飾器的好處就是在不用更改原函式的 前提下給函式增加新的功能。一般而言,我們要想拓展原來函式 最直接的辦法就是侵入 裡面修改,例如 這是我們最原始的的乙個函式,然後我們試圖記錄...
python裝飾裝置 Python裝飾器
裝飾器主要是用來包裝函式,對於一些常用的功能,譬如 日誌列印,函式計時,身份認證。我們可以使用裝飾器來實現,這樣可以降低整個程式的複雜度和減少程式的 量。它實際上就是函式,不同的是,它把乙個函式當做引數,然後返回乙個替代版函式。下面看乙個簡單的示例 defadd number func defadd...
python 函式裝飾 Python 函式裝飾器
無引數的 函式裝飾器 funa 作為裝飾器函式 def funa fn print sakura func a fn 執行傳入的fn引數 print sakura second return sakura return funa def funb print sakurab 返回結果為 sakura...