上一篇文章將經過解決一個需求問題來了解了閉包,本文也將同樣,經過慢慢演變一個需求,一步一步來了解 Python 裝飾器。git
首先有這麼一個輸出員工打卡信息的函數:github
def punch():
print('暱稱:兩點水 部門:作鴨事業部 上班打卡成功')
punch()
複製代碼
輸出的結果以下:編程
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
複製代碼
而後,產品反饋,不行啊,怎麼上班打卡沒有具體的日期,加上打卡的具體日期吧,這應該很簡單,分分鐘解決啦。好吧,那就直接添加打印日期的代碼吧,以下:bash
import time
def punch():
print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
print('暱稱:兩點水 部門:作鴨事業部 上班打卡成功')
punch()
複製代碼
輸出結果以下:微信
2018-01-09
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
複製代碼
這樣改是能夠,但是這樣改是改變了函數的功能結構的,自己這個函數定義的時候就是打印某個員工的信息和提示打卡成功,如今增長打印日期的代碼,可能會形成不少代碼重複的問題。好比,還有一個地方只須要打印員工信息和打卡成功就好了,不須要日期,那麼你又要重寫一個函數嗎?並且打印當前日期的這個功能方法是常用的,是能夠做爲公共函數給各個模塊方法調用的。固然,這都是做爲一個總體項目來考慮的。閉包
既然是這樣,咱們可使用函數式編程來修改這部分的代碼。由於經過以前的學習,咱們知道 Python 函數有兩個特色,函數也是一個對象,並且函數裏能夠嵌套函數,那麼修改一下代碼變成下面這個樣子:函數式編程
import time
def punch():
print('暱稱:兩點水 部門:作鴨事業部 上班打卡成功')
def add_time(func):
print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
func()
add_time(punch)
複製代碼
輸出結果:函數
2018-01-09
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
複製代碼
這樣是否是發現,這樣子就沒有改動 punch
方法,並且任何須要用到打印當前日期的函數均可以把函數傳進 add_time
就能夠了,就好比這樣:學習
import time
def punch():
print('暱稱:兩點水 部門:作鴨事業部 上班打卡成功')
def add_time(func):
print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
func()
def holiday():
print('天氣太冷,今天放假')
add_time(punch)
add_time(holiday)
複製代碼
打印結果:spa
2018-01-09
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
2018-01-09
天氣太冷,今天放假
複製代碼
使用函數編程是否是很方便,可是,咱們每次調用的時候,咱們都不得不把原來的函數做爲參數傳遞進去,還能不能有更好的實現方式呢?有的,就是本文要介紹的裝飾器,由於裝飾器的寫法其實跟閉包是差很少的,不過沒有了自由變量,那麼這裏直接給出上面那段代碼的裝飾器寫法,來對比一下,裝飾器的寫法和函數式編程有啥不一樣。
import time
def decorator(func):
def punch():
print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
func()
return punch
def punch():
print('暱稱:兩點水 部門:作鴨事業部 上班打卡成功')
f = decorator(punch)
f()
複製代碼
輸出的結果:
2018-01-09
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
複製代碼
經過代碼,能知道裝飾器函數通常作這三件事:
但是,認真一看這代碼,這裝飾器的寫法怎麼比函數式編程還麻煩啊。並且看起來比較複雜,甚至有點畫蛇添足的感受。
那是由於咱們尚未用到裝飾器的 「語法糖」 ,咱們看上面的代碼能夠知道, Python 在引入裝飾器 (Decorator) 的時候,沒有引入任何新的語法特性,都是基於函數的語法特性。這也就說明了裝飾器不是 Python 特有的,而是每一個語言通用的一種編程思想。只不過 Python 設計出了 @
語法糖,讓 定義裝飾器,把裝飾器調用原函數再把結果賦值爲原函數的對象名的過程變得更加簡單,方便,易操做,因此 Python 裝飾器的核心能夠說就是它的語法糖。
那麼怎麼使用它的語法糖呢?很簡單,根據上面的寫法寫完裝飾器函數後,直接在原來的函數上加 @
和裝飾器的函數名。以下:
import time
def decorator(func):
def punch():
print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
func()
return punch
@decorator
def punch():
print('暱稱:兩點水 部門:作鴨事業部 上班打卡成功')
punch()
複製代碼
輸出結果:
2018-01-09
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
複製代碼
那麼這就很方便了,方便在咱們的調用上,好比例子中的,使用了裝飾器後,直接在本來的函數上加上裝飾器的語法糖就能夠了,本函數也無虛任何改變,調用的地方也不需修改。
不過這裏一直有個問題,就是輸出打卡信息的是固定的,那麼咱們須要經過參數來傳遞,裝飾器該怎麼寫呢?裝飾器中的函數可使用 *args
可變參數,但是僅僅使用 *args
是不能徹底包括全部參數的狀況,好比關鍵字參數就不能了,爲了能兼容關鍵字參數,咱們還須要加上 **kwargs
。
所以,裝飾器的最終形式能夠寫成這樣:
import time
def decorator(func):
def punch(*args, **kwargs):
print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
func(*args, **kwargs)
return punch
@decorator
def punch(name, department):
print('暱稱:{0} 部門:{1} 上班打卡成功'.format(name, department))
@decorator
def print_args(reason, **kwargs):
print(reason)
print(kwargs)
punch('兩點水', '作鴨事業部')
print_args('兩點水', sex='男', age=99)
複製代碼
輸出結果以下:
2018-01-09
暱稱:兩點水 部門:作鴨事業部 上班打卡成功
2018-01-09
兩點水
{'sex': '男', 'age': 99}
複製代碼
至此,草根學 Python 入門系列文章結束了。若是感興趣的話,能夠關注微信公衆號,回覆 「Python」 獲取更多的 Python 學習資料。