1. 程式人生 > >Python裝飾器基礎及執行時間

Python裝飾器基礎及執行時間

一、裝飾器基礎

裝飾器是可呼叫的物件,其引數是另一個函式(被裝飾的函式)。裝飾器可能會處理被裝飾的函式,然後把他返回,或者將其替換成另一個函式或可呼叫物件。

eg:decorate裝飾器

@decorate
def target():
    print("Running target()")

#上面寫法等同於

def target():
    print("Running target()")
target = decorate(target)

兩種寫法最終得出來的結果相同,但兩個程式碼執行完畢得到的target不一定是原來那個target函式,而是被decorate(target)返回的函式。

1、裝飾器通常把函式替換成另一個函式

def deco(func):
    def inner():
        print('Running inner()')
    return inner     #deco返回inner函式物件

@deco
def target():    #使用deco裝飾target
    print('Running target()')

>>target()    #執行target函式,呼叫被裝飾的target其實會執行inner
Running inner()

>>target    #審查物件,發現target現在是inner的引用
<function deco.<locals>.inner at 0x10063b598>

裝飾器只是一種語法糖,裝飾器可以像常規的可呼叫物件一樣呼叫,其引數是另一個函式。裝飾器的一大特性是,能把被裝飾的函式替換為其他函式,裝飾器在載入模組時會立即執行。

二、何時執行裝飾器

裝飾器在被裝飾的函式定義後會立即執行。

eg:registration.py模組

registry = []    #registry儲存被@registry裝飾的函式引用

def register(func):    #register的引數是一個函式
    print("Running register(%s)" % func)     #顯示被裝飾的函式
    registry.append(func)    #把func存入registry
    return func    #返回func:必須返回函式;這裡返回的函式與通過引數傳入的一樣

@register    #f1,f2被@register裝飾
def f1():
    print("Running f1()")

@register
def f2():
    print("Running f2()")

def f3():    #f3沒用被裝飾
    print("Running f3()")

def main():    #main顯示registry,然後呼叫f1()、f2()、f3()
    print("Running main()")
    print("Registry ->",registry)
    f1()
    f2()
    f3()

if __name__ == "__main__":
    main()    #只有把registration.py當作指令碼執行時才呼叫main()

上述程式碼輸出如下:

$python3 registration.py
Running register(<function f1 at 0x1000631bf8>)
Running register(<function f1 at 0x1000631c80>)
Running main()
registry -> [<function f1 at 0x1000631bf8> ,<function f1 at 0x1000631c80>]
Running f1()
Running f2()
Running f3()

載入模組後,register中有兩個被裝飾函式的引用:f1、f2。這兩個函式,以f3,只有在main明確呼叫它們才執行。

如果匯入registration.py模組(不作為指令碼執行),輸出如下:

import registration
Running register(<function f1 at 0x10063b1e0>)
Running register(<function f2 at 0x10063b237>)

函式裝飾器在匯入模組時立即執行,而被裝飾函式只在明確呼叫時執行