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

Python虛擬機(jī)字節(jié)碼教程之裝飾器實(shí)現(xiàn)詳解

 更新時(shí)間:2023年04月11日 08:56:40   作者:一無(wú)是處的研究僧  
在本篇文章當(dāng)中主要給大家介紹在?cpython?當(dāng)中一些比較常見(jiàn)的字節(jié)碼,從根本上理解?python?程序的執(zhí)行。在本文當(dāng)中主要介紹一些?python?基本操作的字節(jié)碼,并且將從字節(jié)碼的角度分析函數(shù)裝飾器的原理

Python 常見(jiàn)字節(jié)碼

LOAD_CONST

這個(gè)指令用于將一個(gè)常量加載到棧中。常量可以是數(shù)字、字符串、元組、列表、字典等對(duì)象。例如:

>>> dis.dis(lambda: 42)
  1           0 LOAD_CONST               1 (42)
              2 RETURN_VALUE

LOAD_NAME

這個(gè)指令用于將一個(gè)變量加載到棧中。例如:

>>> dis.dis(lambda: x)
  1           0 LOAD_GLOBAL              0 (x)
              2 RETURN_VALUE
>>>

STORE_NAME

這個(gè)指令用于將棧頂?shù)闹荡鎯?chǔ)到一個(gè)變量中。例如:

>>> dis.dis("x=42")
  1           0 LOAD_CONST               0 (42)
              2 STORE_NAME               0 (x)
              4 LOAD_CONST               1 (None)
              6 RETURN_VALUE

BINARY_ADD

這個(gè)指令用于對(duì)棧頂?shù)膬蓚€(gè)值進(jìn)行加法運(yùn)算并將結(jié)果推送到棧中。

>>> dis.dis(lambda: x + y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE

BINARY_SUBTRACT

這個(gè)指令用于對(duì)棧頂?shù)膬蓚€(gè)值進(jìn)行減法運(yùn)算并將結(jié)果推送到棧中。

>>> dis.dis(lambda: x - y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_SUBTRACT
              6 RETURN_VALUE

同樣的加減乘除取余數(shù)的字節(jié)碼如下所示:

>>> dis.dis(lambda: x + y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> dis.dis(lambda: x - y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_SUBTRACT
              6 RETURN_VALUE
>>> dis.dis(lambda: x * y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_MULTIPLY
              6 RETURN_VALUE
>>> dis.dis(lambda: x / y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_TRUE_DIVIDE
              6 RETURN_VALUE
>>> dis.dis(lambda: x // y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_FLOOR_DIVIDE
              6 RETURN_VALUE
>>> dis.dis(lambda: x % y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_MODULO
              6 RETURN_VALUE

COMPARE_OP

這個(gè)指令用于比較棧頂?shù)膬蓚€(gè)值,并且將比較得到的結(jié)果壓入棧中,這個(gè)字節(jié)碼后面后一個(gè)字節(jié)的參數(shù),表示小于大于不等于等等比較符號(hào)。例如:

>>> dis.dis(lambda: x - y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_SUBTRACT
              6 RETURN_VALUE
>>> dis.dis(lambda: x > y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               4 (>)
              6 RETURN_VALUE
>>> dis.dis(lambda: x < y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               0 (<)
              6 RETURN_VALUE
>>> dis.dis(lambda: x != y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               3 (!=)
              6 RETURN_VALUE
>>> dis.dis(lambda: x <= y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               1 (<=)
              6 RETURN_VALUE
>>> dis.dis(lambda: x >= y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               5 (>=)
              6 RETURN_VALUE
>>> dis.dis(lambda: x == y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               2 (==)
              6 RETURN_VALUE

RETURN_VALUE

將棧頂元素彈出作為返回值。

BUILD_LIST

這個(gè)指令用于創(chuàng)建一個(gè)列表。例如:

>>> dis.dis(lambda: [a, b, c, e])
  1           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 LOAD_GLOBAL              2 (c)
              6 LOAD_GLOBAL              3 (e)
              8 BUILD_LIST               4
             10 RETURN_VALUE

這條字節(jié)碼指令有一個(gè)參數(shù)表示??臻g當(dāng)中列表元素的個(gè)數(shù),在上面的例子當(dāng)中這個(gè)參數(shù)是 4 。

BUILD_TUPLE

這個(gè)指令用于創(chuàng)建一個(gè)元組。例如:

>>> dis.dis(lambda: (a, b, c))
  1           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 LOAD_GLOBAL              2 (c)
              6 BUILD_TUPLE              3
              8 RETURN_VALUE

同樣的這個(gè)字節(jié)碼也有一個(gè)參數(shù),表示創(chuàng)建元組的元素個(gè)數(shù)。

BUILD_MAP

這個(gè)指令用于創(chuàng)建一個(gè)字典。例如:

BUILD_SET

和 list 和 tuple 一樣,這條指令是用于創(chuàng)建一個(gè)集合對(duì)象,同樣的這條指令也有一個(gè)參數(shù)表示用于創(chuàng)建集合的元素的個(gè)數(shù)。

>>> dis.dis(lambda: {a, b, c, d})
  1           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 LOAD_GLOBAL              2 (c)
              6 LOAD_GLOBAL              3 (d)
              8 BUILD_SET                4
             10 RETURN_VALUE

BUILD_CONST_KEY_MAP

這條指令是用于創(chuàng)建一個(gè)字典對(duì)象,同樣的這條指令也有一個(gè)參數(shù),表示字典當(dāng)中元素的個(gè)數(shù)。

>>> dis.dis(lambda: {1:2, 3:4})
  1           0 LOAD_CONST               1 (2)
              2 LOAD_CONST               2 (4)
              4 LOAD_CONST               3 ((1, 3))
              6 BUILD_CONST_KEY_MAP      2
              8 RETURN_VALUE

從字節(jié)碼角度分析裝飾器的原理

如果你是一個(gè) pythoner 那么你肯定或多或少聽(tīng)說(shuō)過(guò)裝飾器,這是一個(gè) python 的語(yǔ)法糖我們可以用它來(lái)做很多有趣的事情,比如在不修改源代碼的基礎(chǔ)之上給函數(shù)附加一些功能,比如說(shuō)計(jì)算時(shí)間。

import time
 
def eval_time(func):
    
    def cal_time(*args, **kwargs):
        start = time.time()
        r = func(*args, **kwargs)
        end = time.time()
        return r, end - start
    return cal_time
 
 
@eval_time
def fib(n):
    a = 0
    b = 1
    while n > 0:
        n -= 1
        a, b = b, a + b
    return a

在上面的代碼當(dāng)中我們實(shí)現(xiàn)了一個(gè)計(jì)算斐波拉契數(shù)列的函數(shù),除此之外還寫了一個(gè) eval_time 函數(shù)用于計(jì)算函數(shù)執(zhí)行的時(shí)間,現(xiàn)在調(diào)用函數(shù) fib(10),程序的輸出如下所示:

>>>fib(10)
(55, 5.9604644775390625e-06)

可以看到實(shí)現(xiàn)了我們想要的效果。

現(xiàn)在我們使用一個(gè)更加簡(jiǎn)單的例子來(lái)模擬上面的代碼結(jié)構(gòu),方便我們對(duì)上面函數(shù)執(zhí)行的過(guò)程進(jìn)行分析:

s = """
def decorator(func):
    print("Hello")
    return func
 
@decorator
def fib(n):
    pass
"""
dis.dis(s)

上面的 dis 函數(shù)的輸出對(duì)應(yīng)代碼的字節(jié)碼如下所示:

  2           0 LOAD_CONST               0 (<code object decorator at 0x108068d40, file "<dis>", line 2>)
              2 LOAD_CONST               1 ('decorator')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (decorator)
 
  6           8 LOAD_NAME                0 (decorator)
 
  7          10 LOAD_CONST               2 (<code object fib at 0x1075c1710, file "<dis>", line 6>)
             12 LOAD_CONST               3 ('fib')
             14 MAKE_FUNCTION            0
             16 CALL_FUNCTION            1
             18 STORE_NAME               1 (fib)
             20 LOAD_CONST               4 (None)
             22 RETURN_VALUE
 
Disassembly of <code object decorator at 0x108068d40, file "<dis>", line 2>:
  3           0 LOAD_GLOBAL              0 (print)
              2 LOAD_CONST               1 ('Hello')
              4 CALL_FUNCTION            1
              6 POP_TOP
 
  4           8 LOAD_FAST                0 (func)
             10 RETURN_VALUE
 
Disassembly of <code object fib at 0x1075c1710, file "<dis>", line 6>:
  8           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE

執(zhí)行第一條指令 LOAD_CONST,這條指令主要是加載一個(gè) code object 對(duì)象,這個(gè)對(duì)象里面主要是包含函數(shù) decorator 的字節(jié)碼,主要是上面字節(jié)碼的第二塊內(nèi)容。在執(zhí)行完這條字節(jié)碼之后??臻g如下所示:

執(zhí)行完第二條指令 LOAD_CONST 之后,會(huì)將字符串 decorator 加載進(jìn)入??臻g當(dāng)中。

執(zhí)行第三條指令 MAKE_FUNCTION,這條字節(jié)碼的作用是在虛擬機(jī)內(nèi)部創(chuàng)建一個(gè)函數(shù),函數(shù)的名稱為 decorator,函數(shù)對(duì)應(yīng)的字節(jié)碼則是在先前壓入棧空間當(dāng)中的 code object 對(duì)象,這條指令還會(huì)將創(chuàng)建好的函數(shù)對(duì)象壓入棧中。

STORE_NAME,條字節(jié)碼會(huì)將棧頂?shù)脑貜棾?,并且?co_names[oparg] 指向這個(gè)對(duì)象,在上面的字節(jié)碼當(dāng)中 co_names[oparg] 就是 decorator 。

LOAD_NAME,這條字節(jié)碼就是將 co_names[oparg] 對(duì)應(yīng)的名字指向的對(duì)象重新加載進(jìn)入??臻g當(dāng)中,也就是上面的 decorator 函數(shù)加入進(jìn)行棧空間當(dāng)中。

接下來(lái)的三條字節(jié)碼 LOAD_CONST,LOAD_CONST 和 MAKE_FUNCTION,在執(zhí)行這三條字節(jié)碼之后,棧空間如下所示:

接下來(lái)的一條指令非常重要,這條指令便是裝飾器的核心原理,CALL_FUNCTION 這條指令有一個(gè)參數(shù) i,在上面的字節(jié)碼當(dāng)中為 1,也就是說(shuō)從棧頂開始的前 i 個(gè)元素都是函數(shù)參數(shù),調(diào)用的函數(shù)在??臻g的位置為 i + 1 (從棧頂往下數(shù)),那么在上面的情況下就是說(shuō)調(diào)用 decorator 函數(shù),并且將 fib 函數(shù)作為 decorator 函數(shù)的參數(shù),decorator 函數(shù)的返回值再壓入棧頂。在上面的代碼當(dāng)中 decorator 函數(shù)返回值也是一個(gè)函數(shù),也就是 decorator 函數(shù)的參數(shù),即 fib 函數(shù)。

接下來(lái)便是 STORE_NAME 字節(jié)碼,這條字節(jié)碼的含義我們?cè)谇懊嬉呀?jīng)說(shuō)過(guò)了,就是將棧頂元素彈出,保存到 co_names[oparg] 指向的對(duì)象當(dāng)中,在上面的代碼當(dāng)中也就是將棧頂?shù)膶?duì)象保存到 fib 當(dāng)中。棧頂元素 fib 函數(shù)是調(diào)用函數(shù) decorator 的返回值。

看到這里就能夠理解了原來(lái)裝飾器的最根本的原理不就是函數(shù)調(diào)用嘛,比如我們最前面的用于計(jì)算函數(shù)執(zhí)行時(shí)間的裝飾器的原理就是:

fib = eval_time(fib)

將 fib 函數(shù)作為 eval_time 函數(shù)的參數(shù),再將這個(gè)函數(shù)的返回值保存到 fib 當(dāng)中,當(dāng)然這個(gè)對(duì)象必須是可調(diào)用的,不然后面使用 fib() 就會(huì)保存,我們可以使用下面的代碼來(lái)驗(yàn)證這個(gè)效果。

def decorator(func):
    return func()
 
 
@decorator
def demo():
    return "function demo return string : Demo"
 
print(demo)

執(zhí)行上面的程序結(jié)果為:

function demo return string : Demo

可以看到 demo 已經(jīng)變成了一個(gè)字符串對(duì)象而不再是一個(gè)函數(shù)了,因?yàn)?demo = decorator(demo),而在函數(shù) decorator 當(dāng)中返回值是 demo 函數(shù)自己的返回值,因此才打印了字符串。

總結(jié)

在本篇文章當(dāng)中主要給大家介紹了 python 當(dāng)中一些基礎(chǔ)的字節(jié)碼對(duì)應(yīng)的含義以及示例代碼,本篇文章最重要的便是從字節(jié)碼的角度解釋了裝飾器的本質(zhì)原理,這對(duì)我們以后使用裝飾器非常有幫助,可以靈活的控制和了解裝飾器其中發(fā)生的故事。

以上就是Python虛擬機(jī)字節(jié)碼教程之裝飾器實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Python字節(jié)碼 裝飾器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python安裝pytorch方式

    python安裝pytorch方式

    這篇文章主要介紹了python安裝pytorch方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Python ADF 單位根檢驗(yàn) 如何查看結(jié)果的實(shí)現(xiàn)

    Python ADF 單位根檢驗(yàn) 如何查看結(jié)果的實(shí)現(xiàn)

    這篇文章主要介紹了Python ADF 單位根檢驗(yàn) 如何查看結(jié)果的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-06-06
  • Django模板導(dǎo)入母版繼承和自定義返回Html片段過(guò)程解析

    Django模板導(dǎo)入母版繼承和自定義返回Html片段過(guò)程解析

    這篇文章主要介紹了Django模板導(dǎo)入母版繼承和自定義返回Html片段過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Python使用CRC32實(shí)現(xiàn)校驗(yàn)文件

    Python使用CRC32實(shí)現(xiàn)校驗(yàn)文件

    CRC文件校驗(yàn)是一種用于驗(yàn)證文件完整性的方法,通過(guò)計(jì)算文件的CRC值并與預(yù)先計(jì)算的CRC校驗(yàn)值進(jìn)行比較,來(lái)判斷文件是否發(fā)生變化,本文我們就來(lái)介紹一下Python如何利用CRC32實(shí)現(xiàn)校驗(yàn)文件吧
    2023-10-10
  • Python遠(yuǎn)程控制Windows服務(wù)器的方法詳解

    Python遠(yuǎn)程控制Windows服務(wù)器的方法詳解

    在很多企業(yè)會(huì)使用閑置的 Windows 機(jī)器作為臨時(shí)服務(wù)器,有時(shí)候我們想遠(yuǎn)程調(diào)用里面的程序或查看日志文件。本文分享了利用Python遠(yuǎn)程控制Windows服務(wù)器的方法,感興趣的可以學(xué)習(xí)一下
    2022-05-05
  • Python中format()格式輸出全解

    Python中format()格式輸出全解

    這篇文章主要介紹了Python中format()格式輸出 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • Python Pandas學(xué)習(xí)之基本數(shù)據(jù)操作詳解

    Python Pandas學(xué)習(xí)之基本數(shù)據(jù)操作詳解

    本文將通過(guò)讀取一個(gè)股票數(shù)據(jù),來(lái)進(jìn)行Pandas的一些基本數(shù)據(jù)操作的語(yǔ)法介紹。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2022-02-02
  • Python制作個(gè)性化的詞云圖實(shí)例講解

    Python制作個(gè)性化的詞云圖實(shí)例講解

    大家好,本篇文章主要講的是Python制作個(gè)性化的詞云圖實(shí)例講解,感興趣的同學(xué)趕緊來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • 四行Python3代碼實(shí)現(xiàn)圖片添加美顏效果

    四行Python3代碼實(shí)現(xiàn)圖片添加美顏效果

    這篇文章主要為大家介紹了如何利用Python語(yǔ)言實(shí)現(xiàn)給圖片添加美顏效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2022-04-04
  • Python+selenium破解拼圖驗(yàn)證碼的腳本

    Python+selenium破解拼圖驗(yàn)證碼的腳本

    很多網(wǎng)站在登錄或者注冊(cè)時(shí)都會(huì)遇到拼圖驗(yàn)證碼,這種拼圖驗(yàn)證碼實(shí)際上是多個(gè)小碎片經(jīng)過(guò)重新組合成的一張整體。本文將和大家分享一個(gè)基于Python selenium的破解拼圖驗(yàn)證碼的腳本,需要的可以參考一下
    2022-02-02

最新評(píng)論