Python語法詳解之decorator裝飾器

python 是一門優雅的語言,有些使用方法就像魔法一樣。裝飾器(decorator)就是一種化腐朽性為神奇的技巧。最近一直都在使用 Tornado 框架,一直還是念念不忘 Flask 。Flask 是我最喜歡的 Python 框架,最早被它吸引也是源自它使用裝飾器這個語法糖(Syntactic sugar)來做 Router,讓代碼看上去就感覺甜甜的。

Tornado 中的 Router 略顯平淡,懷念 Flask 的味道,於是很好奇的想知道 Flask 是如何使用這個魔法。通過閱讀 Flask 的源碼,我們也可以為 Tornado 實現瞭一個裝飾器 Router。

當然對於剛接觸 Python 的人,也許很容易理解裝飾器本質是設計模式中的裝飾器模式。可是 Python 通過@一個實現裝飾器的語法糖。下面看下Python語法詳解之decorator裝飾器。

一、定義

裝飾器 decorator 或者稱為包裝器,是對函數的一種包裝。

二、作用

它能使函數的功能得到擴充,而同時不用修改函數本身的代碼。它能夠增加函數執行前、執行後的行為,而不需對調用函數的代碼做任何改變。

三、舉例

初始化函數

# 函數hello,輸出 hello + name 的字符串 
def hello(name):
    return 'hello ' + name  

希望實現功能:在每一個調用 hello 函數的時候,將輸出的字符串用 <tag>包住

例如:hello john 變成 <tag>hello john<tag>

方法一:自定義wrapper函數

這種方法成功修改瞭函數 hello 的行為,不過需要修改對 hello的調用。

每一個調用hello 的地方,都要給成調用wrapper,並修改參數列表

def hello(name):
    return 'hello ' + name
 
def wrapper(tag, func, *arg, **kvargs):
    tag = "<" + tag + ">"
    return tag + func(*arg, **kvargs) + tag
 
if __name__ == "__main__":
    print(wrapper('p', hello, 'john'))

輸出

方法二:自定義decorator函數

為瞭不改變對 hello的調用。我們需要得到一個新的函數對象,它修改 hello的行為,並用這個對象對 hello賦值。

從而調用 hello的時候,調用的是擴充行為後的 hello

def hello(name):
    return 'hello ' + name
def myDecorator(func, tag):
    def myWrapper(*arg, **kvargs):  # 重新包裝func,其參數列表與func一致
        sign = "<" + tag + ">"
        return sign + func(*arg, **kvargs) + sign
 
    return myWrapper
 
hello = myDecorator(hello, "div")  # 用新的函數對象修改hello
 
if __name__ == "__main__":
    print(hello("john")) 

 這樣,隻要hello被myDecorator 賦值一次,以後再調用hello 時,就調用的是包裝後的函數

輸出

方法三:python的decorator

python 的裝飾器所做的事與方式2類似

它通過語法糖使裝飾器看起來更清晰、簡介,而不用每次都書寫方式2中第7行代碼 hello = myDecorator(hello, “div”)

def setTag(tag):  # 由於此裝飾器需要參數,所以要再套一層
    def myDecorator(func):  # 裝飾器的核心,接受函數對象做參數,返回包裝後的函數對象
        def myWrapper(*arg, **kvargs):  # 包裝的具體過程
            sign = "<" + tag + ">"
            return sign + func(*arg, **kvargs) + sign
 
        return myWrapper
 
    return myDecorator
 
 
@setTag("div")  # 用@標簽在定義函數時套上裝飾器
def hello(name):
    return 'hello' + name
 
 
if __name__ == '__main__':
    print(hello('john'))

本質上,方式2 與 方式3 完成的是同一件事,隻不過方式3 比方式2 代碼更簡潔,方便。

比如,現在要給 hello 函數套上三個標簽<body><div><p>

如果用方式2

hello = myDecorator(myDecorator(myDecorator(hello, "body"),"div"),"p")

如果用方式3

@myDecorator("body")
@myDecorator("div")
@myDecorator("p")
def hello(name)
    return 'hello' + name

在多個裝飾器嵌套的情況下,python內置的decorator 結構更清晰。

偽代碼:

def myDecorator(...):    #定義裝飾器,可能帶參數
    def decorator(func):    #裝飾器核心,以被裝飾的函數對象為參數,返回裝飾後的函數對象
        def wrapper(*args, **kvargs):    #裝飾的過程,參數列表適應不同參數的函數
             ...    #修改函數調用前的行為
             func(*args, **kvargs)    #調用函數
             ...    #修改函數調用後的行為
         return wrapper
     return decorator
 
@myDecorator(...):    #給函數加上裝飾器
def myFunc(...):      #自己定義的功能函數
    ...

知識點:

  • 在python中,當*和**符號出現在函數定義的參數中時,表示任意數目參數收集。*arg表示任意多個無名參數,類型為tuple;**kwargs表示關鍵字參數,為dict,使用時需將*arg放在**kwargs之前,否則會有“SyntaxError: non-keyword arg after keyword arg”的語法錯誤
  • 在函數調用時,*會以單個元素的形式解包一個元祖,使其成為獨立的參數。
  • 在函數調用時,**會以鍵/值對的形式解包一個字典,使其成為獨立的關鍵字參數。

到此這篇關於Python語法:decorator裝飾器的文章就介紹到這瞭,更多相關Python decorator裝飾器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: