1. 程式人生 > >總結day12 ----裝飾器

總結day12 ----裝飾器

一,什麼是裝飾器?

裝飾器本質上就是一個python函式,他可以讓其他函式在不需要做任何程式碼變動的前提下,增加額外的功能,裝飾器的返回值也是一個函式物件。

裝飾器的應用場景:比如插入日誌,效能測試,事務處理,快取等等場景。

二,裝飾器的形成過程。

現在我有一個需求,我想讓你測試這個函式的執行時間,在不改變這個函式程式碼的情況下:

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)?這樣還是有點麻煩,因為這些函式的函式名可能是不相同,有func1,func2,graph,等等,所以更簡單的方法,python給你提供了,那就是語法糖。

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

@timer   #==> func1 = timer(func1)
def func1():
    print('in func1')


func1()
裝飾器---語法糖

 剛剛我們討論的裝飾器都是裝飾不帶引數的函式,現在要裝飾一個帶引數的函式怎麼辦呢?

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

@timer
def func1(a):
    print(a)

func1(1)

裝飾器——帶引數的裝飾器
裝飾器---帶引數的裝飾器
import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func1 = timer(func1)
def func1(a,b):
    print('in func1')

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))
裝飾器---hold住所有引數的裝飾器

上面的裝飾器已經非常完美了,但是有我們正常情況下檢視函式資訊的方法在此處都會失效:

複製程式碼
def index():
    '''這是一個主頁資訊'''
    print('from index')

print(index.__doc__)    #檢視函式註釋的方法
print(index.__name__)   #檢視函式名的方法
複製程式碼

如何解決呢?

複製程式碼
from functools import wraps

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

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
print(index.__name__)
複製程式碼

三,開放封閉原則。

1.對擴充套件是開放的

    為什麼要對擴充套件開放呢?

    我們說,任何一個程式,不可能在設計之初就已經想好了所有的功能並且未來不做任何更新和修改。所以我們必須允許程式碼擴充套件、新增新功能。

  2.對修改是封閉的

    為什麼要對修改封閉呢?

    就像我們剛剛提到的,因為我們寫的一個函式,很有可能已經交付給其他人使用了,如果這個時候我們對其進行了修改,很有可能影響其他已經在使用該函式的使用者。

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

四,裝飾器的主要功能和固定結構。

def timer(func):
    def inner(*args,**kwargs):
        '''執行函式之前要做的'''
        re = func(*args,**kwargs)
        '''執行函式之後要做的'''
        return re
    return inner
裝飾器的固定格式
from functools import wraps

def deco(func):
    @wraps(func) #加在最內層函式正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper
裝飾器的固定格式--wraps版

五,帶引數的裝飾器。

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

一個一個的取消掉? 沒日沒夜忙活3天。。。

過兩天你領導想通了,再讓你加上。。。

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''執行函式之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''執行函式之後要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()
帶引數的裝飾器

六,多個裝飾器裝飾一個函式。

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
多個裝飾器裝飾一個函式