Python裝飾器詳解

Python裝飾器詳解

來自專欄面向信仰1 人贊了文章

裝飾器介紹

Python裝飾器是程序開發中經常使用到的功能,熟練掌握裝飾器會讓你的編程思路更加廣闊

要理解裝飾器,首先要先理解:

1. 在 Python 中「函數是一等對象」 。即函數是一種特殊類型的變數,可以和其餘變數一樣,可以作為參數傳遞給函數,也可以作為返回值返回。Python 中的整數、字元串和字典等都是一等對象。

2. 函數裝飾器在導入模塊時立即執行,而被裝飾的函數只在明確調用時運行。


裝飾器實現

裝飾器本質上是python函數,它可以使其他函數在不需要做代碼變動的情況下增加新的功能,裝飾器返回值也是一個函數對象。

以下是一個簡單的例子:

假設現在有一個函數sayHi,用來輸出一句話

def sayHi(): print(Hello, World)s = sayHis()

輸出:Hello, World

我想希望在不修改sayHi函數的情況下在其之前再輸出一句話,這種在代碼運行期間動態增加功能的方式,稱之為「裝飾器」。

本質上,裝飾器就是一個返回函數的高階函數

我們可以這麼寫:

def sayName(func): def inner(): print("Im Yu") return func return inner()def sayHi(): print(Hello, World)s = sayName(sayHi)s()

輸出:

Im YuHello, World

但代碼美中不足的是,我們每次給sayHi增加功能都需要用到類似s = sayName(sayHi)這句話。

python為了簡化這種情況,提供了一個語法糖「@」。

簡化上邊的代碼:

def sayName(func): def inner(): print("Im Yu") return func() return inner@sayNamedef sayHi(): print(Hello, World)sayHi()

輸出:

Im YuHello, World


裝飾器原理

以上代碼中,首先,在裝飾器函數sayName中,sayName需要接受一個參數func,在其內部又定義了一個inner函數,在inner函數中增加一句輸出,並返回func對象,然後sayName函數返回內部函數inner,其實就是一個閉包函數。

接下來在sayHi上邊增加一個@sayName,其意義就是在python解釋器之行到此處時,會調用裝飾器函數(sayName),並把被裝飾得函數(sayHi)作為參數傳入。此時的sayHi已經不是未加裝飾時的函數了,而是指向sayName.inner函數地址。在接下來調用sayHi()時,其實就是調用sayName.inner。


有參函數裝飾

之前的例子是對無參函數的裝飾,如果裝飾帶參數的函數該如何處理?

def sayName(func): def inner(name): print("Im Yu") return func(name) return inner@sayNamedef sayHi(name): print(Hi, + name)sayHi(siri)

輸出:

Im YuHi,siri


兩個裝飾器裝飾函數

這裡測試兩個裝飾器裝飾一個函數的結果:

def sayName(func): print(name) def inner(): print("Im Yu") return func() return innerdef sayAge(func): print(age) def inner(): print("im 30") return func() return inner@sayName@sayAgedef sayHi(): print(Hello, World)sayHi()

輸出:

agenameIm Yuim 30Hello, World

接下來分析輸出這個結果的原因:

首先,python解釋器執行到第一個裝飾器@sayName,在接下來發現裝飾器下邊不是一個函數而是另一個裝飾器,解釋器會執行第二個的裝飾器@sayAge,然後把sayHi函數傳入裝飾器,所以首先輸出了「age」,當@sayAge裝飾完成,此時的sayHi函數地址指向了sayAge.inner的地址,解釋器會返回去執行@sayName裝飾器來裝飾新的sayHi,從而輸出了「name」,接著函數當前指向sayName.inner會先輸出「Im Yu」,在這裡返回的func()其實就是返回的sayAge.inner,所以在下面輸出im 30,最後輸出原本sayHi的「Hello, World」


有參裝飾器

下邊是裝飾器帶參數:

def now(time): def sayName(func): def inner(name): print(現在是: %s % time) print("Im Yu") return func(name) return inner return sayName@now(2016/10/30)def sayHi(name): print(Hello, + name)sayHi(siri)

輸出:

現在是: 2016/10/30Im YuHello,siri


以上就是python裝飾器的相關介紹

推薦閱讀:

函數式語言 Racket 學習記錄
第四屆函數式編程分享會
Rust自虐旅程(一)-- Curry and Compose
classes
如果讓自己只能保留一種編程語言,我選擇……

TAG:Python | 編程 | 函數式編程 |