1. 程式人生 > 實用技巧 >python裝飾器的詳細解析

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() func() 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("hello,here is a func for add :")
    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)))”。

參考:https://www.cnblogs.com/yuzhanhong/p/9180212.html