程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> Python全棧開發之8、裝飾器詳解

Python全棧開發之8、裝飾器詳解

編輯:Python

  一文讓你徹底明白Python裝飾器原理,從此面試工作再也不怕了。

一、裝飾器

  裝飾器可以使函數執行前和執行後分別執行其他的附加功能,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator),裝飾器的功能非常強大,但是理解起來有些困難,因此我盡量用最簡單的例子一步步的說明這個原理。

1、不帶參數的裝飾器

  假設我定義了一個函數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')              # 1、首先打印了字符‘start’,
        r=func()                    # 2、執行func函數,func函數相當於def f(): print('中') 
        print('end')                # 3、接著函數打印‘end’
        return r                    # 4、將func函數的結果返回
    return inner

@outer
def f():              # f=outer(f)=innner
    print('中')

f()                   # f()相當於inner(),執行inner函數的步驟看上面定義處的注釋
#打印結果順序為 start 中 end

2、包含任意參數的裝飾器

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

#其實只要將上面一種不帶參數的裝飾器修改一下就可以了
#修改也很簡單,只需將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 f(a,b):
    print(a+b)
f(1,4)                    # f(1,4)相當於inner(1,4) 這裡打印的結果為 start 5 end

3、使用兩個裝飾器

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

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                                #1、f=outer1(f) f被重新賦值為outer1(1)的返回值inner1,
def f():                               #    此時func1為 f():print('f 函數')
    print('f 函數')                     #2、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('結束')

4、帶參數的裝飾器  

  前面的裝飾器本身沒有帶參數,如果要寫一個帶參數的裝飾器怎麼辦,那麼我們就需要寫一個三層的裝飾器,而且前面寫的裝飾器都不太規范,下面來寫一個比較規范帶參數的裝飾器,下面來看一下代碼,大家可以將下面的代碼自我運行一下

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)
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved