Python全棧---5.1---裝飾器

1、裝飾器

  執行outer函數,將index做爲參數傳遞,python

  將outer函數的返回值,從新賦值給index編程

  裝飾器能夠在函數執行前和執行後執行其餘的附加功能,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator),裝飾器的功能很是強大,可是理解起來有些困難,所以我儘可能用最簡單的例子一步步的說明這個原理。app

  寫代碼要遵循開發封閉原則,雖然在這個原則是用的面向對象開發,可是也適用於函數式編程,簡單來講,它規定已經實現的功能代碼不容許被修改,但能夠被擴展,即:函數式編程

  • 封閉:已實現的功能代碼塊
  • 開放:對擴展開發

一、不帶參數的裝飾器:

  假設我定義了一個函數f,想要在不改變原來函數定義的狀況下,在函數運行前打印出start,函數運行後打印出end,要實現這樣一個功能該怎麼實現?看下面如何用一個簡單的裝飾器來實現:函數

# 使用@語法放在函數的定義上面 至關於執行 f=outer(f),此時f賦值成爲了一個新的outer函數, 
# 此時f函數就指向了outer函數的返回值inner,inner是一個函數名,定義在oute函數裏面 
# 原來的f是函數名可簡單理解爲一個變量,做爲outer函數的參數傳遞進去了 此時參數func至關於f 
def outer(func):                    # 定義一個outer函數做爲裝飾器 
    def inner():            # 若是執行inner()函數的話步驟以下: 
        print('start')              # 一、首先打印了字符‘start’, 
        r=func()                    # 二、執行func函數,func函數至關於def f(): print('中')  
        print('end')                # 三、接着函數打印‘end’ 
        return r                    # 四、將func函數的結果返回 
    return inner 
  
@outer
def f():              # f=outer(f)=innner 
    print('中') 
  
f()                   # f()至關於inner(),執行inner函數的步驟看上面定義處的註釋<BR>#打印結果順序爲   start 中 end 

 

二、包含任意參數的裝飾器:

  在實際中,咱們的裝飾器可能應用到不一樣的函數中去,這些函數的參數都不同,那麼咱們怎麼實現一個對任意參數都能實現功能的裝飾器?還記得我寫函數那篇博客中,就寫一種能夠接受任意參數的函數,下面來看看如何將其應用到裝飾器中去。spa

#其實只要將上面一種不帶參數的裝飾器修改一下就能夠了 
#修改也很簡單,只需將inner和func的參數改成 (*args,**kwargs) 
#其餘實現的過程和上面一種同樣,就再也不介紹了 
def outer(func): 
    def inner(*args,**kwargs): 
        print('start') 
        r=func(*args,**kwargs)    # 這裏func(*args,**kwargs)至關於f(a,b) 
        print('end') 
        return r 
    return inner 
  
@outer
def index(a,b): 
    print(a+b) 
m=index(1,4)                    # f(1,4)至關於inner(1,4) 這裏打印的結果爲 start 5 end 
print m

 

一、outer函數加載
二、遇到outer裝飾器
三、執行outer函數,將index函數(被裝飾的函數)做爲參數傳遞func=index()
四、加載inner函數,
五、將outer函數的返回值inner(這是一個函數),從新賦值給index(也就是說此時的index指向inner函數)
六、執行inner函數
七、打印‘start’
八、執行res = func(a1, a2)(也就是執行被裝飾器裝飾的函數(index函數))
九、將index函數的返回值賦值給res
十、打印‘end’
十一、將inner函數的返回值 res 賦值給index函數
十二、打印m

  

三、使用兩個裝飾器:

  當一個裝飾器不夠用的話,咱們就能夠用兩個裝飾器,固然理解起來也就更復雜了,當使用兩個裝飾器的話,首先將函數與內層裝飾器結合而後在與外層裝飾器相結合,要理解使用@語法的時候到底執行了什麼,是理解裝飾器的關鍵。這裏仍是用最簡單的例子來進行說明。3d

def outer2(func2): 
    def inner2(*args,**kwargs): 
        print('開始') 
        r=func2(*args,**kwargs) 
        print('結束') 
        return r 
    return inner2 
  
def outer1(func1): 
    def inner1(*args,**kwargs): 
        print('start') 
        r=func1(*args,**kwargs) 
        print('end') 
        return r 
    return inner1 
  
@outer2                                # 這裏至關於執行了 f=outer1(f)  f=outer2(f),步驟以下 
@outer1                                #一、f=outer1(f) f被從新賦值爲outer1(1)的返回值inner1, 
def f():                               #    此時func1爲 f():print('f 函數') 
    print('f 函數')                     #二、f=outer2(f) 相似f=outer2(inner1) f被從新賦值爲outer2的返回值inner2 
                                       #    此時func2 爲inner1函數 inner1裏面func1函數爲原來的 f():print('f 函數') 
                                                                           
f()                                    # 至關於執行 outer2(inner1)()  
>>開始                                  # 在outer函數裏面執行,首先打印 ‘開始 ’ 
>>start                                # 執行func2 即執行inner1函數 打印 ‘start’ 
>>f 函數                               # 在inner1函數裏面執行 func1 即f()函數,打印 ‘f 函數’  
>>end                                  # f函數執行完,接着執行inner1函數裏面的 print('end') 
>>結束                                 # 最後執行inner2函數裏面的 print('結束') 

四、帶參數的裝飾器:

  前面的裝飾器自己沒有帶參數,若是要寫一個帶參數的裝飾器怎麼辦,那麼咱們就須要寫一個三層的裝飾器,並且前面寫的裝飾器都不太規範,下面來寫一個比較規範帶參數的裝飾器,下面來看一下代碼,你們能夠將下面的代碼自我運行一下orm

import functools 
  
def log(k=''):                                        #這裏參數定義的是一個默認參數,若是沒有傳入參數,默認爲空,能夠換成其餘類型的參數 
    def decorator(func): 
        @functools.wraps(func)                        #這一句的功能是使被裝飾器裝飾的函數的函數名不被改變, 
        def wrapper(*args, **kwargs): 
            print('start') 
            print('{}:{}'.format(k, func.__name__))    #這裏使用了裝飾器的參數k 
            r = func(*args, **kwargs) 
            print('end') 
            return r 
        return wrapper 
    return decorator 
  
@log()                        # fun1=log()(fun1) 裝飾器沒有使用參數 
def fun1(a): 
    print(a + 10) 
  
fun1(10) 
# print(fun1.__name__)        # 上面裝飾器若是沒有@functools.wraps(func)一句的話,這裏打印出的函數名爲wrapper 
  
@log('excute')                # fun2=log('excute')(fun2) 裝飾器使用給定參數 
def fun2(a): 
    print(a + 20) 
fun2(10) 

 雙層裝飾器:

 一個函數能夠被多個裝飾器裝飾嗎? 好比兩個裝飾器 對象

 會先將 函數交個最下層@裝飾器將處理結果在交給其上一層@裝飾器  即先交給w2 再交給w1blog

 

def w1(func):
    def inner(*args,**kwargs):
        # 驗證1
        # 驗證2
        # 驗證3
        return func(*args,**kwargs)
    return inner
 
def w2(func):
    def inner(*args,**kwargs):
        # 驗證1
        # 驗證2
        # 驗證3
        return func(*args,**kwargs)
    return inner
 
 
@w1
@w2
def f1(arg1,arg2,arg3):
    print 'f1'

雙層裝飾器原理

 

相關文章
相關標籤/搜索