python裝飾器的詳細解析


什么是裝飾器?

 

python裝飾器(fuctional decorators)就是用於拓展原來函數功能的一種函數,目的是在不改變原函數名(或類名)的情況下,給函數增加新的功能。 

這個函數的特殊之處在於它的返回值也是一個函數,這個函數是內嵌“原“”函數的函數。


一般而言,我們要想拓展原來函數代碼,最直接的辦法就是侵入代碼里面修改,例如:

import time
def f():
    print("hello")
    time.sleep(1)
    print("world")  

 

這是我們最原始的的一個函數,然后我們試圖記錄下這個函數執行的總時間,那最簡單的做法就是改動原來的代碼:

import time
def f():
    start_time = time.time()
    print("hello")
    time.sleep(1)
    print("world")
    end_time = time.time()

    execution_time = (end_time - start_time)*1000
    print("time is %d ms" %execution_time)

 

但是實際工作中,有些時候核心代碼並不可以直接去改,所以在不改動原代碼的情況下,我們可以再定義一個函數。(但是生效需要再次執行函數)

import time

def deco(func):
    start_time = time.time()
    f()
    end_time = time.time()
    execution_time = (end_time - start_time)*1000
    print("time is %d ms" %execution_time)

def f():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':

    deco(f)
    print("f.__name__ is",f.__name__)
    print() 

 

這里我們定義了一個函數deco,它的參數是一個函數,然后給這個函數嵌入了計時功能。但是想要拓展這一千萬個函數功能,

就是要執行一千萬次deco()函數,所以這樣並不理想!接下來,我們可以試着用裝飾器來實現,先看看裝飾器最原始的面貌。 

import time

def deco(f):
    def wrapper():
        start_time = time.time()
        f()
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" %execution_time )
    return wrapper

@deco
def f(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': f()
 

這里的deco函數就是最原始的裝飾器,它的參數是一個函數,然后返回值也是一個函數。

其中作為參數的這個函數f()就在返回函數wrapper()的內部執行。然后在函數f()前面加上@deco,

f()函數就相當於被注入了計時功能,現在只要調用f(),它就已經變身為“新的功能更多”的函數了,

(不需要重復執行原函數)。 

擴展1:帶有固定參數的裝飾器

import time

def deco(f):
    def wrapper(a,b):
        start_time = time.time()
        f(a,b)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
    return wrapper

@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

if __name__ == '__main__':
    f(3,4)

 

擴展2:無固定參數的裝飾器

import time

def deco(f):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time_ = (end_time - start_time)*1000
        print("time is %d ms" %execution_time)
    return wrapper


@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

@deco
def f2(a,b,c):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b+c))


if __name__ == '__main__':
    f2(3,4,5)
    f(3,4)

 

擴展3:使用多個裝飾器,裝飾一個函數

import time

def deco01(f):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
        print("deco01 end here")
    return wrapper

def deco02(f):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        f(*args, **kwargs)

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))


if __name__ == '__main__':
    f(3,4)
 ''' this is deco01 this is deco02 hello,here is a func for add : result is 7 deco02 end here time is 1003 ms deco01 end here '''

裝飾器調用順序

裝飾器是可以疊加使用的,那么使用裝飾器以后代碼是啥順序呢?

對於Python中的”@”語法糖,裝飾器的調用順序與使用 @ 語法糖聲明的順序相反。

在這個例子中,”f(3, 4) = deco01(deco02(f(3, 4)))”。

 

Python內置裝飾器

在Python中有三個內置的裝飾器,都是跟class相關的:staticmethod、classmethod 和property。

  • staticmethod 是類靜態方法,其跟成員方法的區別是沒有 self 參數,並且可以在類不進行實例化的情況下調用
  • classmethod 與成員方法的區別在於所接收的第一個參數不是 self (類實例的指針),而是cls(當前類的具體類型)
  • property 是屬性的意思,表示可以通過通過類實例直接訪問的信息

對於staticmethod和classmethod這里就不介紹了,通過一個例子看看property。

注意,對於Python新式類(new-style class),如果將上面的 “@var.setter” 裝飾器所裝飾的成員函數去掉,則Foo.var 屬性為只讀屬性,使用 “foo.var = ‘var 2′” 進行賦值時會拋出異常。但是,對於Python classic class,所聲明的屬性不是 read-only的,所以即使去掉”@var.setter”裝飾器也不會報錯。

總結

本文介紹了Python裝飾器的一些使用,裝飾器的代碼還是比較容易理解的。只要通過一些例子進行實際操作一下,就很容易理解了。


參考鏈接:https://blog.csdn.net/xiangxianghehe/article/details/77170585


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM