python裝飾器詳解,多層裝飾器,及帶參數的裝飾器。


pyhon學習有一段時間了,今天又碰到了Django的中間件,其實Django里面的中間件,就是用了多層的裝飾器,然后去了解了一下多層裝飾器,感覺有寫東西差不多快忘了,也可能前面沒學好。

現在重新記錄,學習下。

 

普通裝飾器

def warp(func):
    print('我是裝飾器,碰到需要裝飾的函數,一開始執行這里')
    def inner(*args, **kwargs):
        print('這里才是真正的裝飾開始!')
        res = func(*args, **kwargs)
        print('裝飾結束')
        return res
    print('我這里是外圍,先出去了,里面的等需要的時候執行')
    return inner

@warp   # 裝飾器符號


def demo(x, y):
    return x + y

if __name__ == '__main__':
    demo(1, 2)

 

運行結果:


我是裝飾器,碰到需要裝飾的函數,一開始執行這里
我這里是外圍,先出去了,里面的等需要的時候執行
這里才是真正的裝飾開始!
裝飾結束

 

其實我前面多層裝飾卡住就是這里。

裝飾器的本質就是一個函數,當你寫好裝飾器的函數從上而下運行時,一旦遇到@的提醒,python應該就開始進入准備狀態尋找需要裝飾的函數,當遇到下面一個需要被裝飾的函數demo時,

整個過程我在debug過程中發現,其實python馬上跳轉到開始去執行了warp函數,這就是剛開始我們輸出前面兩句的原因,其實這個時候,程序根本沒有執行到demo(1,2)的地方。

在這個warp 程序的運行過程中,還發生一個命名轉換的過程,就是demo=warp(),其實就是demo等於warp執行返回的函數inner,所以最后面執行demo(1,2)的時候,裝飾器開始工作。

理解了這個,后面多層的裝飾器更加好理解了。

 

下面上一個最簡單的兩層裝飾器。

def deco1(func):
    print('func 1 in')
    def wrapper1():
        print('wrap1 in')
        func()
        print('wrap1 out')
    print('func 1 out')
    return wrapper1

def deco2(func):
    print('func 2 in')
    def wrapper2():
        print('wrap2 in')
        func()
        print('wrap2 out')
    print('func 2 out')
    return wrapper2

@deco1
@deco2
def foo():
    print('foo')


if __name__ == '__main__':
    foo()

 執行結果為:

func 2 in
func 2 out
func 1 in
func 1 out
wrap1 in
wrap2 in
foo
wrap2 out
wrap1 out

其實一開我因為沒能理解裝飾器的原理在兩個地方卡住了,第一個就是為什么先出的是

func 2 in
func 2 out
func 1 in
func 1 out

第二個問題就是為什么foo函數執行了一次,第二問題,網上查到了資料,第一個問題,通過對裝飾的理解和debug的過程,也理解了。

通過介紹,整個程序是至上而下運行的,在運行過程到

@deco1

@deco2

def foo():

 一遇到需要裝飾的函數def的時候,用要開始重新走裝飾器,在走的過程中,先走跟函數近的裝飾器deco2,所以輸出了
func 2 in
func 2 out
再走deco1,所以輸出了
func 1 in
func 1 out

兩個裝飾器函數執行后,其實也運行了變量名替換。foo=deco1()即deco1程序執行的返回值wapper1,重點是來了wappper1里面的func其實已經成為deco2()即deco2執行程序的返回值wapper2,wapper2里面的func才是真正需要被裝飾的函數foo。
所以在warp1 in后面執行func的時候,直接跳到warp2了。
所有在整個多層裝飾的執行中,裝飾器的執行是至上而下的,但在裝飾器的(可以說內部調試中)是從哪個靠近需要被裝飾的函數,哪個先執行,簡單來說就是至下而上的。
通過這樣的理解,其實無論多少層裝飾器,真正被裝飾的原始函數將在最下面的那個裝飾器執行,另外的只不過在一層接着一層的調用函數,其實也可以認為在一次次運行裝飾過程。
整個過程有點像壓棧跟彈棧
最后一個帶參數的裝飾器。
這個更加像一個內部返回來兩次函數的定義函數,一共需要三個def來寫這個函數。由於日常使用不過,我的理解可能也不是很充分。

 

def deco(params):     # params 為需要傳入的參數
    print('floor1')
    def inner(func):
        print('floor2')
        def warp(*args, **kwargs):
            print('floor3')
            print('裝飾開始')
            for i in range(params):
                func(*args, **kwargs)
            print('裝飾結束')
            print('out3')
        print('out2')
        return warp
    print('out1')
    return inner


@deco(5)        #這個就是生成一個函數warp指向demo

def demo():
    print('ok')

if __name__ == '__main__':
    demo()

 執行結果:

/Users/shijianzhong/Desktop/swiper/.venv/bin/python /Users/shijianzhong/Desktop/swiper/paramsfunc.py
floor1
out1
floor2
out2
floor3
裝飾開始
ok
ok
ok
ok
ok
裝飾結束
out3

Process finished with exit code 0

根據前面分析的,我廢話少了點,其實在調試階段,由於是帶參數的裝飾器,所以在這個三層def的情況,裝飾器前期調試就已經運行了兩層,且命名demo指向warp函數。

正常情況下,前面的兩種情況理解,這個帶參數的應該理解不難。

按照我的理解,可以先認為,在調試的過程中,裝飾器自己先運行了一遍把函數返回了回來,然后用重命名了一下,這個帶參數的,可以認為是裝飾器自己運行了兩遍,其中一遍是把參數寫入內存中,待最里面的也就是真正需要裝飾的函數使用。

 

今天就寫這些。裝飾器這下應該問題不大了,有空把閉包函數也寫一下。

按照我的理解,可以先認為,在調試的過程中,裝飾器自己先運行了一遍把函數返回了回來,然后用重命名了一下,這個帶參數的,可以認為是裝飾器自己運行了兩遍,其中一遍是把參數寫入內存中,待最里面的也就是真正需要裝飾的函數使用。
[Ànzhào wǒ de lǐjiě, kěyǐ xiān rènwéi, zài tiáoshì de guòchéng zhōng, zhuāngshì qì zìjǐ xiān yùnxíngle yībiàn bǎ hánshù fǎnhuíle huílái, ránhòu yòng zhòng mìngmíngle yīxià, zhège dài cān shǔ de, kěyǐ rènwéi shì zhuāngshì qì zìjǐ yùnxíngle liǎng biàn, qízhōng yībiàn shì bǎ cānshù xiě rù nèicún zhōng, dài zuì lǐmiàn de yě jiùshì zhēnzhèng xūyào zhuāngshì de hánshù shǐyòng.]
As I understand it, you can believe that, in the debugging process, decorator their first run again the function returns back, and then rename the moment, the arguments can be considered decorator own run twice where the parameter is again written to memory, the innermost of which is to be true to be decorated function uses.


免責聲明!

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



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