淺談 Python 中的Decorator(上)

有了它,程式碼看起來更優雅

Tsung-Yu
Tom’s blog
6 min readFeb 21, 2020

--

圖為Flask的route配置

前言

之前專題寫Linebot時用Flask串接Linebot的SDK,一開始只是照個官方給的Sample Code去架設Linebot,後來深入研究Sample Code後一直不太理解裡面@的意涵,利用空檔撰寫這篇文章,加深對Decorator的觀念。

Decorator中文翻作裝飾器,裝飾Python中的classfunction,它其實是Python的一種語法糖(簡化寫法),可重複利用程式碼,將程式碼化繁為簡,@當做開頭。

Decorator背後牽涉到很重要的觀念 — 閉包(Closure)

閉包的部分之後在記錄成一篇筆記~本文主要初步了解Decorator的概念

實作

Smaple Code(Decorator)

來看簡單範例:

執行上面的範例結果,可以發現函式看起來好像沒有東西被呼叫,卻自動被執行?
那如果改成呼叫my_name()函式的話:

呼叫my_name()會報TypeError: 'NoneType' object is not callable的錯誤訊息

還原Sample Code (不加Decorator)

上述有使用@做簡化的範例程式碼,如果還原成不加Decorator的話:

由上述例子可知:

function也可作為參數傳遞並執行。

前後比較有無Decorator的範例發現,有Decorator的程式碼省略掉name = print_my_name(my_name)
透過範例,可以把它簡單理解為:

對一個函式進行打包的動作,後續可重複呼叫打包過的函式。

利用function可作參數傳入的特性,將my_name()作為參數傳入print_my_name(name)中(做為decorator)。

原因

name = print_my_name(my_name) 表示把my_name這個 function傳入print_my_name做處理,再把 print_my_name(my_name) 的回傳值assign給變數name

一開始的my_name是function,但是因為重新賦值的關係,my_name() 已經不是一個function,而是與 print_my_name(my_name)的回傳值連結在一起。

因為print_my_name沒有指定回傳的值,python會預設return None,最後執行print(my_name)時就會得到None的結果。

解決方法

針對剛才加入Decorator範例中,呼叫my_name()報錯問題,只需在加上@的函式裡面做return,回傳由外部傳入的參數即可!

或是單純不想印出錯誤訊息的話可以在呼叫函式時刪除()

分析函數

舉剛才未使用Decorator範例,若印出name的話:

回傳值會是一個function物件

若要執行function物件的內容,需改成print_my_name(my_name)(),因為print_my_name(my_name)只會return function自己,必須在後面加上()來呼叫。

以上是目前整理Python裝飾器(Decorator)初步的用法,其實裝飾器的寫法總共有四種,更多Python Decorator的進階用法留待下一篇。

如果觀念上有不正確或是文章內容有錯誤之處

還請看過文章的人指教!

參閱

--

--