day 11 – 1 裝飾器

亦雙弓發表於2018-12-04

裝飾器

裝飾器形成的過程:最簡單的裝飾器——有返回值的——有一個引數——萬能引數
裝飾器的作用:不想修改函式的呼叫方式 但是還想在原來的函式前後新增功能
原則:開放封閉原則
語法糖:@裝飾器函式名
裝飾器的固定模式

原則:開放封閉原則
開放:對擴充套件是開放的
封閉:對修改是封閉的

首先我們來逐步實現這麼一個功能吧

計算程式碼執行的時間

#先來看下需要的元件
import time  #time 模組

time.time()       #獲取當前時間
time.sleep(5)      #讓程式睡眠多少時間

print(time.time()) #來試下

我們來寫一個可以計算時間的函式

def func():
    start=time.time()
    time.sleep(0.1)   #時間太短 系統會顯示 0.0,所以我們讓他休眠 0.1s
    print("計算程式執行的時間")
    end=time.time()
    print(end-start)

func()

但是這裡有一個問題:如果要計算很多函式的執行時間,那不是要在函式中都加上幾行計算的程式碼,這顯然是不可行的,一般寫好的沒問題的函式,是不會去對它進行修改,而且還很麻煩

所以這裡我們寫一個計算時間的函式獨立出來

def times(f):
    start=time.time()
    time.sleep(0.1)
    f()
    end=time.time()
    print(end-start)


def func():
    print("計算程式執行的時間")


def func2():
    print(time.sleep(5))

times(func)
times(func2)

但是這樣貌似還有一個問題,我要計算時間的函式都要呼叫 times() 函式,這樣其實是很麻煩的

最終還是要呼叫 func() 就可以計算執行時間,就是說你呼叫的是 func() 其實呼叫的 times() 這就很完美了

接著我們來了解下裝飾器吧,解決這樣的問題

先來寫一個簡單的裝飾器

#一個簡單的裝飾器
import time
def func():
    time.sleep(0.1)
    print(`你好,世界`)

def timmer(f):  #裝飾器函式
    def inner():
        start = time.time()
        f()    #被裝飾的函式
        end = time.time()
        print(end - start)
    return inner

func = timmer(func)    #通過這一步就可以使用 func 呼叫 timmer 了
func()

我們這裡做的事情就是:在不想修改函式的呼叫方式的情況下  但還想在原來的函式前後新增功能

接著我們來看另一個甜甜的東西——語法糖

import time

def timmer(f):  #裝飾器函式
    def inner():
        start = time.time()
     time.sleep(0.1)  f()   
#被裝飾的函式 end = time.time() print(end - start) return inner @timmer #語法糖 替代下面 func = timmer(func) 這句話 def func(): time.sleep(0.1) print(`你好,世界`) #func = timmer(func) #通過這一步就可以使用 func 呼叫 timmer 了 func()

 

然後我們來看接收返回值的裝飾器

import time

def timmer(f):      #裝飾器函式
    def inner():
        start=time.time()
        time.sleep(0.1)
        ret = f()         #被裝飾的函式
        end=time.time()
        print(end-start)
        return ret  #接收 func() 函式的返回值    
    return inner    #這個地方要穿函式的名字 而不是加括號

@timmer             #語法糖 替代下面 func = timmer(func) 這句話
def func():
    print(`你好,世界`)
    return `Hello`

print(func())       #返回值列印  返回值為:Hello

 

最後我們來看帶引數的裝飾器,一個引數與萬能引數是一樣的,只是引數的不同罷了

#一個引數的裝飾器
import time

def timmer(f):      #裝飾器函式
    def inner(a):   #3.給 inner() 加上引數
        start=time.time()
        time.sleep(0.1)
        ret = f(a)  #4.由於 inner() 把引數傳給了 f(),所以 f() 也要加上引數,最後測試傳值
        end=time.time()
        print(end-start)
        return ret  #接收 func() 函式的返回值
    return inner

@timmer             #語法糖 替代下面 func = timmer(func) 這句話
def func(a):        #2.這裡也要加上引數,由於執行 func 時,實際執行的 inner(),所以 inner() 也要加上引數
    print("計算程式執行的時間",a)
    return `Hello`

print(func("123"))  #1.加入引數

#輸出結果
```
計算程式執行的時間 123
0.10937380790710449
Hello
```
###萬能引數的裝飾器
#可以看出 只是在 a 的位置更換了 *args **kwargs 來接收引數
import time

def timmer(f):      
    def inner(*args,**kwargs):   
        start=time.time()
        time.sleep(0.1)
        ret = f(*args,**kwargs)
        end=time.time()
        print(end-start)
        return ret  
    return inner

@timmer             
def func(*args,**kwargs):        
    print("計算程式執行的時間",args,kwargs)    #2. 就是這個位置的 kwargs,如果寫了,列印時會出現一個 ` {} ` 空字典,並不會報錯
    return `Hello`

print(func(`1`,`2`,`3`,k=100))    #1. 不需要關鍵字傳參時,kwargs 可以不寫

#輸出結果
```
計算程式執行的時間 (`1`, `2`, `3`) {`k`: 100}
0.1093745231628418
Hello
```

好了,以上就是我們對於裝飾器完善的最終結果了

 

裝飾器的固定模式

def wrapper(f):     #裝飾器函式,f是被裝飾的函式
    def inner(*args,**kwargs):    #定義一個內建函式,且和下面統級別的返回值名字一致且不加括號
                                  #動態引數一定要原封不動的傳給 被裝飾的函式 f()
        ```在被裝飾函式之前要做的事```
        ret = f(*args,**kwargs)   #被裝飾的函式  函式執行完的返回值要原封不動傳給 return
        ```在被裝飾函式之後要做的事```
        return ret
    return inner

@wrapper         #語法糖 @裝飾器函式名 等價於 func = wrapper(func)
def func(a,b):     #被裝飾的函式
    time.sleep(0.01)
    print(`你好:世界`,a,b)
    return `新年好`
#print(func(23,45))  #接收返回值
ret = func(23,45)   #接收返回值
print(ret)

相關文章