如何通俗理解python中的裝飾器?

裝飾器詳解

裝飾器,顧名思義就是裝飾的意思,就是不改變裏面的牆體結構,只是在外面增加了一層隔音板或者牆紙。

對應程序也是這樣的,不改變原函數的結構功能,調用函數的接口也沒有變化,但是就是增多了該函數的功能。這是怎麼做到的呢?本次用一個例子來全面說明。

我們已經寫好了一個函數是index,現在給該函數index加上該函數運行的時間的功能。

1、原始代碼

# -*- coding=utf-8 -*-
import time

def index():  
    time.sleep(2)

startTime = time.time()
index()   #f1,f2...fn
endTime = time.time()
print(endTime - startTime) #輸出2.000297784805298

上面代碼表示運行index函數花費的時間,這就有一個問題了,如果我要計算函數f1(),函數f2(),函數fn()…那麼每一個函數都要這麼寫,就會重複很多代碼,這是寫程序不允許的,因此需要一個函數來進行包裝,也就是說decorator就是一個返回函數的高階函數。

2、增加函數

import time
def index():
    time.sleep(2)

def calculate_time():
    startTime = time.time()
    index()
    endTime = time.time()
    print(endTime - startTime)

calculate_time() #out:2.000297784805298

上面這麼寫就是用了一個函數,還是不能解決之前提到的問題,換了index(),還得重新寫calculate_time函數,那麼此時我們需要用到將函數傳遞到函數裏面。

# -*- coding=utf-8 -*-
import time

def index():
    time.sleep(2)

def calculate_time(f):
    startTime = time.time()
    f()
    endTime = time.time()
    print(endTime - startTime)

calculate_time(index)

index()傳過去,那麼之後需要修改index就很方便,但是這個也不能完全解決之前的問題,即我改變的函數的調用接口,之前的函數是index,現在的函數變成了calculate_time,這也也很不習慣,因此我們需要進一步升級。

3、加入閉包

這一步就是在函數裏面添加函數

import time

def index():
    time.sleep(2)

def calculate_time(f):
    def inner():
        startTime = time.time()
        f()
        endTime = time.time()
        print(endTime - startTime)
    return inner

index = calculate_time(index)
index()

這個寫法和上面的例子是一致的,且調用的時候,我們只是用了index(),這就是裝飾器了,不過需要在前面加一句話index = calculate_time(index)
所以我們需要python中的語法糖@

# -*- coding=utf-8 -*-
import time

def calculate_time(f):
    def inner():
        startTime = time.time()
        f()
        endTime = time.time()
        print(endTime - startTime)
    return inner

@calculate_time #index = calculate_time(index)
def index():
    time.sleep(2)

index()

需要知道 @calculate_time 等價於 index = calculate_time(index)

4、加入輸出變量

一般函數都是由return輸出的,本例子加入index輸出結果,如下:

import time

def calculate_time(f):
    def inner():
        startTime = time.time()
        result = f()  #f函數是有輸出的,符合一般的函數寫法
        endTime = time.time()
        print(endTime - startTime)
        return result
    return inner

@calculate_time #index = calculate_time(index)
def index():
    time.sleep(2)
    return 'index'

res = index()
print(res) # out:2.0000038146972656 index

5、升級優化傳遞函數

如果我們需要在寫入一個其他的函數,且各個函數輸入不一樣,那麼此時需要對函數的傳遞參數進行改進,如下:

# -*- coding=utf-8 -*-
import time

def calculate_time(f):
    def inner(*args,**kwargs):
        startTime = time.time()
        result = f(*args,**kwargs)
        endTime = time.time()
        print(endTime - startTime)
        return result
    return inner

@calculate_time
def index():
    time.sleep(2)
    return 'index'

@calculate_time
def add(x,y):
    time.sleep(2)
    return x + y

res = index()
addRes = add(1,2)
print(res,addRes)

輸出:

2.000779151916504
2.000826597213745
index 3

*args,**kwargs傳遞到函數裏面,之後就可以隨意寫自己需要被裝飾的函數了。

6、總結

一個標準的裝飾器如下:

def calculate_time(f):
    def inner(*args,**kwargs):
        startTime = time.time()
        result = f(*args,**kwargs)
        endTime = time.time()
        print(endTime - startTime)
        return result
    return inner

先把功能寫好,比如上面的計算時間差。然後調用我們的需要被裝飾的函數f(*args,**kwargs),並給出一個返回值,且需要把內層函數作爲結果返回出去。那麼最後我們的 index = calculate_time(index)等價於@calculate_time

整個便是裝飾器的內容了。
記住: @calculate_time 等價於 index = calculate_time(index)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章