Python基礎之裝飾器

1、什麼是裝飾器?

裝飾器本質上就是一個Python函數,它可讓其它函數在不須要作任何代碼變更的前提下,增長額外的功能,裝飾器的返回值也是一個函數對象。python

裝飾器的應用場景:好比插入日誌,性能測試,事物處理,緩存等等場景。緩存

2、裝飾器的造成過程

如今我有一個需求,我想讓你在不改變函數代碼的狀況下,測試出這個函數的執行時間:app

import time


def func1():
    print("in func1")


def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner


func1 = timer(func1)
func1()

可是若是有多個函數,我都想讓你測試他們的執行時間,你豈不是每一個函數都要寫一遍func1 = timer(func1)?ide

這樣寫很是麻煩,由於這些函數的函數名都是不相同的,有func一、func2,func3等等。函數

針對這種狀況,python給咱們提供了一個簡單快捷的方法,那就是語法糖。性能

 1 import time
 2 
 3 
 4 def timer(func):
 5     def inner():
 6         start = time.time()
 7         func()
 8         print(time.time() - start)
 9     return inner
10 
11 
12 @timer  # ==> func1 = timer(func1)
13 def func1():
14     print("in func1")
15     
16     
17 func1()
裝飾器 ——語法糖

剛剛咱們討論的裝飾器都是裝飾不帶參數的函數,如今要裝飾一個帶參數的函數要怎麼辦呢?測試

 1 import time
 2 
 3 def timer(func):
 4     def inner(a):
 5         start = time.time()
 6         func(a)
 7         print(time.time() - start)
 8     return inner
 9 
10 
11 @timer
12 def func1(a):
13     print(a)
14 
15 func1(1)
裝飾器——帶參數的裝飾器
 1 import time
 2 
 3 
 4 def timer(func):
 5     def inner(*args, **kwargs):
 6         start = time.time()
 7         result = func(*args, **kwargs)
 8         print(time.time() - start)
 9         return result
10     return inner
11 
12 
13 @timer #==> func1 = timer(func1)
14 def func1(a, b):
15     print("in func1")
16     
17 
18 @timer #==> func2 = timer(func2)
19 def func2(a):
20     print("in func2 and get a:%s" % a)
21     return "func2 end"
22 
23 
24 func1("aaa", "bbb")
25 print(func2("aa"))
裝飾器——hold住全部參數的裝飾器

上面的裝飾器已經很是完美了,可是咱們在正常狀況下查看函數信息的方法卻在此處所有失效了:this

def index():
    """
    這是一個主頁信息
    :return:
    """
    print("from index")


print(index.__doc__)    # 查看函數註釋的方法
print(index.__name__)   # 查看函數名的方法

那麼如何解決這個問題呢?spa

from functools import wraps


def deco(func):
    @wraps(func)    # 放在最內層函數最上方
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@deco
def index():
    """
    顯示首頁信息
    :return:
    """
    print("from index")


print(index.__doc__)
print(index.__name__)

3、開放封閉原則

裝飾器是完美的遵循了這個開放封閉原則的。設計

那麼什麼是開放封閉原則呢?

咱們能夠從下面兩個方面來看。

1. 對擴展是開放的

爲何要對擴展開放呢?

咱們說,任何一個程序,不可能在設計之初就已經想好了全部的功能,而且將來不作任何更新和修改。因此咱們必須容許代碼擴展、添加新功能。

2. 對修改是封閉的

爲何要對修改封閉呢?

就像咱們剛剛提到的,由於咱們寫的一個函數,頗有可能已經交付給其餘人使用了,若是這個咱們對其進行了修改,頗有可能影響其餘已經在使用該函數的用戶。

4、裝飾器的主要功能和固定結構

def timer(func):
    def inner(*args, **kwargs):
        """執行函數以前要作的"""
        result = func(*args, **kwargs)
        """執行函數以後要作的"""
        return result
    return inner
裝飾器的固定格式——普通版
1 from functools import wraps
2 
3 def deco(func):
4     @wraps(func)    # 加在最內層函數最上方
5     def wrapper(*args, **kwargs):
6         return func(*args, **kwargs)
7     return wrapper
裝飾器的固定格式——wrapper版

5、帶參數的裝飾器

假如你有成千上萬個函數使用了一個裝飾器,如今你想把這些裝飾器都取消掉,你要怎麼作?

若是一個一個的取消,那任務量也太大了吧。

萬一,沒過幾天,你又須要用這些裝飾器,豈不是要吐血。

那麼解決辦法,就是在裝飾器上加上參數:

 1 def wrapper_out(flag):
 2     def wrapper(func):
 3         @wraps(func)
 4         def inner(*args, **kwargs):
 5             if flag:
 6                 print("執行函數以前要作的")
 7 
 8             result = func(*args, **kwargs)
 9 
10             if flag:
11                 print("執行函數以後要作的")
12 
13             return result
14         return inner
15     return wrapper
16 
17 
18 @wrapper_out(False)     # 經過傳遞True和False來控制裝飾器內部的運行效果
19 def func():
20     pass
21 
22 
23 func()
帶參數的裝飾器

6、多個裝飾器裝飾一個函數

先執行下面這樣一個代碼

 1 def wrapper1(func):
 2     def inner(*args, **kwargs):
 3         print("111")
 4         result = func(*args, **kwargs)
 5         print("222")
 6         return result
 7     return inner
 8 
 9 
10 def wrapper2(func):
11     def inner(*args, **kwargs):
12         print("333")
13         result = func(*args, **kwargs)
14         print("444")
15         return result
16     return inner
17 
18 
19 @wrapper2
20 @wrapper1
21 def func():
22     print("this is func")
23 
24 func()
多個裝飾器裝飾一個函數

當執行完畢後,能夠看到執行結果爲:

333
111
this is func
222
444

執行順序:首先@warpper1裝飾器來,而後獲取到一個新函數是wrapper1中的inner,而後執行@wrapper2。這個時候,wrapper2裝飾的就是wrapper1中的inner了。

因此,執行順序就像:第二層裝飾器前(第一層裝飾器前(目標)第一層裝飾器後)第二層裝飾器後。程序從左到右執行起來,這就是咱們看到的結果。

相關文章
相關標籤/搜索