python裝飾器入門到精通

你會Python嘛?
我會!
那你給我講下Python裝飾器吧!
Python裝飾器啊?我沒用過哎

  • 1
  • 2
  • 3
  • 4

簡言之,python裝飾器就是用於拓展原來函數功能的一種函數,這個函數的特殊之處在於它的返回值也是一個函數,使用python裝飾器的好處就是在不用更改原函數的代碼前提下給函數增加新的功能。
一般而言,我們要想拓展原來函數代碼,最直接的辦法就是侵入代碼裏面修改,例如:

import time
def func():
    print("hello")
    time.sleep(1)
    print("world")
  • 1
  • 2
  • 3
  • 4
  • 5

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

#原始侵入,篡改原函數
import time
def func():
    startTime = time.time()

    print("hello")
    time.sleep(1)
    print("world")
    endTime = time.time()

    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

但是如果你的Boss在公司裏面和你說:“小祁,這段代碼是我們公司的核心代碼,你不能直接去改我們的核心代碼。”那該怎麼辦呢,我們仿照裝飾器先自己試着寫一下:

#避免直接侵入原函數修改,但是生效需要再次執行函數
import time

def deco(func):
    startTime = time.time()
    func()
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)


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

if __name__ == '__main__':
    f = func
    deco(f)#只有把func()或者f()作爲參數執行,新加入功能纔會生效
    print("f.__name__ is",f.__name__)#f的name就是func()
    print()
    #func()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

這裏我們定義了一個函數deco,它的參數是一個函數,然後給這個函數嵌入了計時功能。然後你可以拍着胸脯對老闆說,看吧,不用動你原來的代碼,我照樣拓展了它的函數功能。
然後你的老闆有對你說:“小祁,我們公司核心代碼區域有一千萬個func()函數,從func01()到func1kw(),按你的方案,想要拓展這一千萬個函數功能,就是要執行一千萬次deco()函數,這可不行呀,我心疼我的機器。”
好了,你終於受夠你老闆了,準備辭職了,然後你無意間聽到了裝飾器這個神器,突然發現能滿足你閆博士的要求了。
我們先實現一個最簡陋的裝飾器,不使用任何語法糖和高級語法,看看裝飾器最原始的面貌:

#既不需要侵入,也不需要函數重複執行
import time

def deco(func):
    def wrapper():
        startTime = time.time()
        func()
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':
    f = func #這裏f被賦值爲func,執行f()就是執行func()
    f()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

這裏的deco函數就是最原始的裝飾器,它的參數是一個函數,然後返回值也是一個函數。其中作爲參數的這個函數func()就在返回函數wrapper()的內部執行。然後在函數func()前面加上@deco,func()函數就相當於被注入了計時功能,現在只要調用func(),它就已經變身爲“新的功能更多”的函數了。
所以這裏裝飾器就像一個注入符號:有了它,拓展了原來函數的功能既不需要侵入函數內更改代碼,也不需要重複執行原函數。

#帶有參數的裝飾器
import time

def deco(func):
    def wrapper(a,b):
        startTime = time.time()
        func(a,b)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))

if __name__ == '__main__':
    f = func
    f(3,4)
    #func()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

然後你滿足了Boss的要求後,Boss又說:“小祁,我讓你拓展的函數好多可是有參數的呀,有的參數還是個數不定的那種,你的裝飾器搞的定不?”然後你嘿嘿一笑,深藏功與名!

#帶有不定參數的裝飾器
import time

def deco(func):
    def wrapper(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))

@deco
def func2(a,b,c):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b+c))


if __name__ == '__main__':
    f = func
    func2(3,4,5)
    f(3,4)
    #func()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

最後,你的老闆說:“可以的,小祁,我這裏一個函數需要加入很多功能,一個裝飾器怕是搞不定,裝飾器能支持多個嘛”
最後你就把這段代碼丟給了他:

#多個裝飾器

import time

def deco01(func):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
        print("deco01 end here")
    return wrapper

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

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))



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

'''
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
'''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

多個裝飾器執行的順序就是從最後一個裝飾器開始,執行到第一個裝飾器,再執行函數本身。

盜用評論裏面一位童鞋的例子:

def dec1(func):  
    print("1111")  
    def one():  
        print("2222")  
        func()  
        print("3333")  
    return one  

def dec2(func):  
    print("aaaa")  
    def two():  
        print("bbbb")  
        func()  
        print("cccc")  
    return two  

@dec1  
@dec2  
def test():  
    print("test test")  

test()  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

輸出:

aaaa  
1111  
2222  
bbbb  
test test  
cccc  
3333
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

參考:Python 裝飾器:http://python.jobbole.com/82344/

轉載:https://blog.csdn.net/xiangxianghehe/article/details/77170585

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章