Python 裝飾器是在面試過程高頻被問到的問題,
裝飾器也是一個非常好用的特性,
熟練掌握裝飾器會讓你的編程思路更加寬廣,
程序也更加 pythonic。
![用世界盃的方式,讓你理解 Python 裝飾器,有點長!](http://p2.ttnews.xyz/loading.gif)
今天我們就結合世界盃來帶大家理解裝飾器
德國戰車
6 月 17 日德國戰墨西哥
小痴雖然是一個偽球迷,但每年的世界盃還是會了解下。
而德國是上屆的冠軍,又是這屆奪冠熱門。
德意志戰車在 32 年間小組賽就沒有輸過!
臥槽!
雖然小痴很少賭球,但這次德國如此強大,
肯定會贏吧。
搏一搏單車變摩托!
隨後小痴買了德國隊贏。
心裡想著這次肯定穩了!贏了會所嫩模!
小痴連比賽都不看,
美滋滋的敲著代碼。
![用世界盃的方式,讓你理解 Python 裝飾器,有點長!](http://p2.ttnews.xyz/loading.gif)
然後比賽結果卻是德國爆冷 0:1 輸給墨西哥隊,
德國隊輸了比賽
只是此時的天台有點擠,
風還有大。
含淚的寫下了下面的代碼:
輸出結果:
裝飾器是什麼
首先我們先來了解下什麼是裝飾器,
嚴格來說,
裝飾器只是語法糖,裝飾器是可調用的對象,
可以像常規的可調用對象那樣調用,
特殊的地方是裝飾器的參數是一個函數。
裝飾器的存在是為了適用兩個場景,
一個是增強被裝飾函數的行為,
另一個是代碼重用。
比如在上面的例子中我們在壓德國隊贏的時候,
原本的 german_team() 函數只是輸出德國必勝,
但在使用裝飾器(guess_win)後,
它的功能多了一項:
輸出「天台已滿,請排隊!」。
這就是一個簡單的裝飾器,實現了「增強被裝飾函數的行為」。
一個良好的裝飾器必須要遵守兩個原則:
- 不能修改被裝飾函數的代碼
- 不能修改被裝飾函數的調用方式
這裡並不難以理解,
在現在的生產環境中,很多代碼是不能輕易的改寫,
因為這樣有可能發送意想不到的影響。
還有一點就是我們在看大神的代碼,
我們根本不懂如何改寫。
同時你也不能修改調用方式,
因為你並不知道有在一個項目中,
有多少處應用了此函數。
裝飾器理解基礎
如果你想要很好的理解裝飾器,
那下面的兩個內容需要你先有所認知:
- 函數名可以賦值給變量
- 高階函數
1. 函數名可以賦值給變量
我們來看下這個例子:
輸出結果:
在代碼中我們首先定義了函數 func,
並調用了 func 函數,並且把 func 賦值給 y。
y = func 表明了:
函數名可以賦值給變量,並且不影響調用。
這樣講,
可能還有些人不太明白。
我們在來對比下我們常用的操作。
這其實和整數、數字是一樣的,
下面的代碼你肯定熟悉:
2 .高階函數
高階函數滿足如下的兩個條件中的任意一個:
a.可以接收函數名作為實參;
b.返回值中可以包含函數名。
在 Python 標準庫中的 map 和 filter 等函數就是高階函數。
輸出結果:
自定義一個能返回函數的函數,
也是高階函數:
輸出結果:
實現一個類似的裝飾器
現在你已經知道了「函數名賦值」和「高階函數」,
有了這兩個基礎,
我們就可以嘗試實現一個類似的裝飾器。
輸出結果:
在這個例子中我們定義了一個 status 函數,
status 接收一個函數名然後直接返回該函數名。
這樣我們實現了不修改原函數 name,
並且添加了一個新功能的需求。
但是這裡有個缺陷就是函數的調用方式改變了。
即不是原本的 name,而是 temp。
要解決這個問題很簡單,
相信 a = a*3 這樣的表達式大家都見過,
那麼上述代碼中的 temp = status(name) 同樣可以修改為 name = status(name),
這樣我們就完美的解決了問題:
既添加新功能又沒有修改原函數和其調用方式。
修改後的代碼如下:
但這樣的代碼卻有個不便之處,即每次使用這樣的裝飾器,
我們都要寫類似 name = status(name) 的代碼。
程序員都是懶的,
所以才有那麼多高級的語法。
在 python 中為了簡化這種情況,
提供了一個語法糖 @,
在每個被裝飾的函數上方使用這個語法糖就可以省掉這一句代碼 name = status(name),
最後的代碼如下:
這樣我們就弄清楚了裝飾器的工作原理:
- 寫一個高階函數,即參數是函數,返回的也是函數。
- 在利用語法糖@,簡化賦值操作。
但是對比開頭的例子,還是有些不一樣。
在開始的例子中,
我們還實現了一個 rooftop_status 函數,
來判斷下當前的天台狀是否人滿。
但是我們現在是直接返回了函數名,
這樣函數調用後我們就沒辦法做任何事情。
梅西和德國慌了,我們也慌了,
各個都要天台見,
但在這之前我們也要考慮下天台的情況。
為了能判斷天台的情況,所以此時我們需要在嵌套一層函數,
將實現額外功能的部分寫在內層函數中,
然後將這個內層函數返回即可。
這也是為什麼裝飾器都是嵌套函數的原因。
另外,
開篇的例子並沒有返回值,也沒有參數,
要對既有參數又有返回值的函數進行裝飾的話,
還需要進一步完善。
能夠處理返回值的裝飾器:
輸出結果:
能夠處理參數的裝飾器:
輸出結果:
總結
裝飾器的本質是函數,其參數是另一個函數(被裝飾的函數)。
裝飾器通常會額外處理被裝飾的函數,
然後把它返回,
或者將其替換成另一個函數或可調用對象。
行為良好的裝飾器可以重用,以減少代碼量。
對於這屆的世界盃,我總結了下。
python小白可以私信我回復【01】有基礎教程
閱讀更多 煙火照長空o 的文章