欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中多個(gè)裝飾器執(zhí)行順序驗(yàn)證深入講解

 更新時(shí)間:2025年10月06日 10:55:17   作者:普通網(wǎng)友  
裝飾器是Python中一種強(qiáng)大的語(yǔ)法結(jié)構(gòu),本質(zhì)上是一個(gè)特殊的函數(shù),它的主要作用是在不改變?cè)瘮?shù)代碼的基礎(chǔ)上,為其他函數(shù)或類添加額外的功能,這篇文章主要介紹了Python中多個(gè)裝飾器執(zhí)行順序驗(yàn)證的相關(guān)資料,需要的朋友可以參考下

關(guān)于 Python 裝飾器執(zhí)行時(shí)的順序問(wèn)題,一直以來(lái)都保持粗略的理解概念:

  1. 裝飾器相當(dāng)于函數(shù)調(diào)用的語(yǔ)法糖,因此在函數(shù)執(zhí)行時(shí),會(huì)從最內(nèi)層括號(hào)開(kāi)始,逐層向外執(zhí)行。從代碼文本上看,就是距離被修飾函數(shù)越近的裝飾器,越先執(zhí)行
  2. 原始的裝飾器會(huì)覆蓋被修飾函數(shù)的__name__等元數(shù)據(jù),需要使用functools.wraps修飾wrapper函數(shù),保留被修飾函數(shù)的元數(shù)據(jù)
  3. Python 代碼存在編譯時(shí)運(yùn)行時(shí)的區(qū)別,因此,裝飾器中代碼的執(zhí)行順序,不總是被裝飾函數(shù)最先執(zhí)行

最近遇到一些問(wèn)題,對(duì)以上概念的理解有些模糊,嘗試通過(guò)代碼驗(yàn)證。執(zhí)行 Python 版本為 3.9.12。

單模塊執(zhí)行

測(cè)試代碼

#!/usr/bin/python
# encoding: utf-8

def deco1(func):
    print('Deco 1-1')
    def wrapper(*args, **kwargs):
        print('Deco 1 - wrapper 1')
        r = func(*args, **kwargs)
        print('Deco 1 - wrapper 2')
        return r
    print('Deco 1-2')
    return wrapper


def deco2(func):
    print('Deco 2-1')
    def wrapper(*args, **kwargs):
        print('Deco 2 - wrapper 1')
        r = func(*args, **kwargs)
        print('Deco 2 - wrapper 2')
        return r
    print('Deco 2-2')
    return wrapper


def deco3(func):
    print('Deco 3-1')
    def wrapper(*args, **kwargs):
        print('Deco 3 - wrapper 1')
        r = func(*args, **kwargs)
        print('Deco 3 - wrapper 2')
        return r
    print('Deco 3-2')
    return wrapper


@deco3
@deco2
@deco1
def test():
    print("Test func")


if __name__ == '__main__':
    print('Test begin')
    test()
    print('Test end')

測(cè)試代碼中,deco1、deco2 和 deco3 裝飾器都是同步函數(shù),只有輸出日志是不同的,因此,可以認(rèn)為輸出日志的順序,即是代碼執(zhí)行的順序。

執(zhí)行結(jié)果

代碼執(zhí)行結(jié)果如下:

Deco 1-1
Deco 1-2
Deco 2-1
Deco 2-2
Deco 3-1
Deco 3-2
Test begin
Deco 3 - wrapper 1
Deco 2 - wrapper 1
Deco 1 - wrapper 1
Test func
Deco 1 - wrapper 2
Deco 2 - wrapper 2
Deco 3 - wrapper 2
Test end

執(zhí)行結(jié)果分析

分析執(zhí)行結(jié)果的順序可以發(fā)現(xiàn):

  1. 進(jìn)入模塊__main__入口之前,會(huì)先“編譯”裝飾器代碼,因此 "Test begin" 之前會(huì)打印Deco 1-1 等日志
  2. 在“Test begin”執(zhí)行后,Deco 3 - wrapper 1 -> Deco 2 - wrapper 1 -> Deco 1 - wrapper 1 依次出現(xiàn),與裝飾器從上到下的應(yīng)用順序相符,而“Test func”之后,Deco 1 - wrapper 2 -> Deco 2 - wrapper 2 -> Deco 3 - wrapper 2 依次執(zhí)行,順序變成后進(jìn)先出
  3. 所有裝飾器代碼執(zhí)行結(jié)束,才輪到最后的“Test end”

多個(gè)裝飾器的“編譯”順序,符合“距離最近的先執(zhí)行”的印象

如果調(diào)整裝飾器的應(yīng)用順序,讓 deco2 最先修飾 test,關(guān)鍵代碼如下:

@deco3
@deco1
@deco2
def test():
    print("Test func")

執(zhí)行時(shí)輸出結(jié)果如下,可以發(fā)現(xiàn)“Test begin”之前的部分中,deco2最先被調(diào)用

Deco 2-1
Deco 2-2
Deco 1-1
Deco 1-2
Deco 3-1
Deco 3-2
Test begin
Deco 3 - wrapper 1
Deco 1 - wrapper 1
Deco 2 - wrapper 1
Test func
Deco 2 - wrapper 2
Deco 1 - wrapper 2
Deco 3 - wrapper 2
Test end

結(jié)論 v1.0

分析上面代碼的執(zhí)行順序,可以得到以下結(jié)論:

  1. 執(zhí)行被裝飾的函數(shù)之前,存在類似“編譯”的階段,會(huì)執(zhí)行裝飾器中 wrapper 函數(shù)之外的代碼
  2. 多個(gè)裝飾器修飾同一個(gè)函數(shù)的情況下,最外層(即寫(xiě)在函數(shù)代碼塊最上面一行)的裝飾器的 wrapper 函數(shù)中,被裝飾函數(shù)之前的代碼最先被執(zhí)行,然后是次外層的裝飾器中被裝飾函數(shù)之前的代碼,依此類推,直到真正執(zhí)行被裝飾函數(shù);如果 wrapper 函數(shù)內(nèi)存在執(zhí)行后的代碼,則執(zhí)行順序與 wrapper 函數(shù)內(nèi)被裝飾函數(shù)執(zhí)行前代碼的執(zhí)行順序相反,可以認(rèn)為全部代碼的執(zhí)行順序符合deco3(deco2(deco1(test())))的函數(shù)式執(zhí)行順序,與文檔說(shuō)明相符

多模塊執(zhí)行

測(cè)試裝飾器是否應(yīng)用編譯緩存。將裝飾器代碼與測(cè)試代碼放在不同的模塊中。

測(cè)試代碼

decos.py

#!/usr/bin/python
# encoding: utf-8

def deco1(func):
    print('Deco 1-1')
    def wrapper(*args, **kwargs):
        print('Deco 1 - wrapper 1')
        r = func(*args, **kwargs)
        print('Deco 1 - wrapper 2')
        return r
    print('Deco 1-2')
    return wrapper


def deco2(func):
    print('Deco 2-1')
    def wrapper(*args, **kwargs):
        print('Deco 2 - wrapper 1')
        r = func(*args, **kwargs)
        print('Deco 2 - wrapper 2')
        return r
    print('Deco 2-2')
    return wrapper


def deco3(func):
    print('Deco 3-1')
    def wrapper(*args, **kwargs):
        print('Deco 3 - wrapper 1')
        r = func(*args, **kwargs)
        print('Deco 3 - wrapper 2')
        return r
    print('Deco 3-2')
    return wrapper

decorator-test.py

#!/usr/bin/python
# encoding: utf-8

from decos import deco1, deco2, deco3

@deco2
def test2():
    print("Test 2 func")


@deco3
@deco2
@deco1
def test():
    print("Test func")

if __name__ == '__main__':
    print('Test begin')
    test2()  # 連續(xù)執(zhí)行多個(gè)被裝飾函數(shù)
    print("==" * 10)
    test()
    print('Test end')

執(zhí)行結(jié)果

Deco 2-1
Deco 2-2
Deco 1-1
Deco 1-2
Deco 2-1
Deco 2-2
Deco 3-1
Deco 3-2
Test begin
Deco 2 - wrapper 1
Test 2 func
Deco 2 - wrapper 2
====================
Deco 3 - wrapper 1
Deco 2 - wrapper 1
Deco 1 - wrapper 1
Test func
Deco 1 - wrapper 2
Deco 2 - wrapper 2
Deco 3 - wrapper 2
Test end

結(jié)果分析

分析以上執(zhí)行結(jié)果,可以發(fā)現(xiàn):

  1. Deco 2-1出現(xiàn)了兩次,可以認(rèn)為多個(gè)被裝飾函數(shù)一起被執(zhí)行時(shí),裝飾器 wrapper 函數(shù)之外的代碼會(huì)被重復(fù)執(zhí)行,執(zhí)行順序符合代碼中被裝飾函數(shù)的聲明順序
  2. wrapper 函數(shù)內(nèi),被裝飾函數(shù)前后的代碼,它們的執(zhí)行順序與單模塊的情況下相同

結(jié)論

Python 中的裝飾器相當(dāng)于語(yǔ)法糖,實(shí)際執(zhí)行時(shí),代碼執(zhí)行順序與多層函數(shù)包裹目標(biāo)函數(shù)的寫(xiě)法的執(zhí)行順序一致(即 deco3(deco2(deco1(test()))) )。

在簡(jiǎn)單裝飾器的場(chǎng)景下,例如裝飾器的 wrapper 函數(shù)中直接return func(**args, **kwargs)的寫(xiě)法,裝飾器函數(shù)本身、內(nèi)部 wrapper 函數(shù)中不包含額外邏輯的情況下,可以認(rèn)為被裝飾函數(shù)先執(zhí)行并返回結(jié)果,然后越靠近被裝飾函數(shù)的裝飾器越先返回。

如果裝飾器本身包含較復(fù)雜的邏輯,例如測(cè)試代碼中 wrapper 函數(shù)執(zhí)行func前后都存在其他邏輯的寫(xiě)法,則需要參考多層函數(shù)的執(zhí)行順序,wrapper 函數(shù)之外的代碼會(huì)先于被執(zhí)行代碼執(zhí)行,wrapper 函數(shù)中func前的代碼會(huì)按從最外層裝飾器到最內(nèi)層的順序執(zhí)行,func后的代碼則反過(guò)來(lái),最內(nèi)層裝飾器的先執(zhí)行,符合多層函數(shù)調(diào)用堆棧的抽象。

到此這篇關(guān)于Python中多個(gè)裝飾器執(zhí)行順序驗(yàn)證的文章就介紹到這了,更多相關(guān)Python多個(gè)裝飾器執(zhí)行順序驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論