day11-裝飾器

1.舉例講解

  這裏先導入time模塊的概念。

import  time
print(time.time()) # 打印距離1970年到現在的秒數
time.sleep(1) # 停留一秒再執行下一句
'''
測試一段代碼執行會經過多長時間
'''
import time
def func(): # 被裝飾的函數
    time.sleep(0.01) # 由於一個print語句太少,所以這裏故意設置一個時間間隔,以達到預期效果。
    print('今天好熱')

def timmer(f): # 裝飾器函數
    def inner():
        start = time.time()
        f() # 調用被裝飾的函數
        end = time.time()
        print('時間間隔為:',end - start)
    return inner

result = timmer(func)
result()

  上面一段代碼就是一個簡單的裝飾器。

  通常為了是代碼更加優美,於是引入語法糖的概念。這裏將對上述第二段代碼進行改進,他們的作用和結果完全相同。

'''
測試一段代碼執行會經過多長時間
'''
import time

def timmer(f): # 裝飾器函數
    def inner():
        start = time.time()
        f() # 調用被裝飾的函數
        end = time.time()
        print('時間間隔為:',end - start)
    return inner

@timmer #語法糖-@裝飾器函數
def func(): # 被裝飾的函數
    time.sleep(0.01) # 由於一個print語句太少,所以這裏故意設置一個時間間隔,以達到預期效果。
    print('今天好熱')

# result = timmer(func) @timeer與此句一個作用相同
func() # 分析一下,此func等價於timmer(func),也就相當於inner。

《day11-裝飾器》
《day11-裝飾器》

'''
測試一段代碼執行會經過多長時間
'''
# 新增功能-裝入帶參數函數的裝飾器
import time

def timmer(f): # 裝飾器函數
    def inner(a):
        start = time.time()
        f(a) # 調用被裝飾的函數
        end = time.time()
        print('時間間隔為:',end - start)
    return inner

@timmer #語法糖-@裝飾器函數
def func(a): # 被裝飾的函數
    time.sleep(0.01) # 由於一個print語句太少,所以這裏故意設置一個時間間隔,以達到預期效果。
    print('今天好熱',a)

# result = timmer(func) @timeer與此句一個作用相同
func(1) # 分析一下,此func等價於timmer(func),也就相當於inner。

裝入帶參數函數的裝飾器

  上面一段代碼是裝入一個參數,如果我想在被裝飾函數中加入兩個參數呢?也許你會說,再加入一個變量,那麼三個參數呢?這個時候我們就要用到前面學過的內容:*args。繼續,如果我們再加入一個關鍵字參數呢?同樣,我們要用到**kwargs。下面是代碼:

'''
測試一段代碼執行會經過多長時間
'''
# 新增功能-裝入帶參數函數的裝飾器
import time

def timmer(f): # 裝飾器函數
    def inner(*args,**kwargs):
        start = time.time()
        f(*args,**kwargs) # 調用被裝飾的函數
        end = time.time()
        print('時間間隔為:',end - start)
    return inner

@timmer #語法糖-@裝飾器函數
def func(a,b): # 被裝飾的函數
    time.sleep(0.01) # 由於一個print語句太少,所以這裏故意設置一個時間間隔,以達到預期效果。
    print('今天好熱',a,b)

# result = timmer(func) @timeer與此句一個作用相同
func(1,b = 3) # 分析一下,此func等價於timmer(func),也就相當於inner。

 

2.裝飾器的作用

  (1)不想修改函數的調用方式,但是還想在原來的函數前後添加功能。

  (2)timmer就是一個裝飾器函數,只是對一個函數有一些裝飾作用。

3.原則-開放封閉原則

  對擴展是開放的。

  對修改是封閉的。

4.裝飾器進階

4.1._name_、_doc_

# 學習._name_和._doc_,在這裏如果不做任何修改,._name_會輸出inner,因為study和inner等價,但是我們做出以下修改,就會成功輸出study
from functools import wraps #導入
def wrapper(func):
    @wraps(func)# 加上這句
    def inner(*args,**kwargs):
        print('在被裝飾的函數執行前要做的事')
        ret = func(*args,**kwargs)
        print('在被裝飾的函數執行后要做的事')
        return ret
    return inner

@wrapper
def study(days):
    '''
    這是一個註釋
    :param days:
    :return:
    '''
    print('堅持學習%s天'%days)
    return '加油'

# ret = study(10)
# print(ret)

print(study.__name__)# 輸出
print(study.__doc__)

4.2.多個函數下的裝飾器

# 若多個函數同時用一個裝飾器,一般來說就是在函數上面加一個@裝飾器名,但是我們有不想用它的時候,如果我們一個一個去刪除它則會非常的麻煩,下面這種方法將會解決這種問題。
import time
Flag = False

def timmer_out(Flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if Flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timmer
@timmer_out(Flag)
def para_fir():
    time.sleep(0.1)
    print('the first')

@timmer_out(Flag)
def para_sec():
    time.sleep(0.1)
    print('the second')

para_fir()
para_sec()
# 思想就是在裝飾器外再加上一層,成為三層裝飾器,通過判斷Flag為True還是False,來執行相應的代碼塊

4.3.多個裝飾器

def wrapper1(func):  # func-->f
    def inner1():
        print('wrapper1 ,before func')
        ret = func()
        print('wrapper1 ,after func')
        return ret
    return inner1

def wrapper2(func): # func = inenr1
    def inner2():
        print('wrapper2 ,before func')
        ret = func()
        print('wrapper2 ,after func')
        return ret
    return inner2


@wrapper2 # f = wrapper2(f) -> wrapper2(inner1) = inner2
@wrapper1 # f = wrapper1(f) = inner1
def f():
    print('in f')
    return '哈哈哈'

print(f()) # f = inner2

# result:
# wrapper2 ,before func
# wrapper1 ,before func
# in f
# wrapper1 ,after func
# wrapper2 ,after func
# 哈哈哈

# 仔細觀察這個結果,首先執行wrapper1,將f傳入wrapper1中,於是wrapper1中的func就是f,最後返回一個inner1。接着再執行wrapper2,但是此時傳入wrapper2中的參數是上一次執行返回過來的inner1,所以wrapper2中的func是inner1,最後返回一個inner2。
# 為什麼結果是先有wrapper2,但是實際上是先執行wrapper1呢,這是因為裝飾器中的語法糖會找最近的一個被修飾的函數,顯然wrapper1更接近f(),而wrapper2比較遠,所以是先執行wrapper1,再是wrapper2。

 

点赞

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *