Python虛擬機(jī)字節(jié)碼教程之裝飾器實(shí)現(xiàn)詳解
Python 常見字節(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ù)表示棧空間當(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 那么你肯定或多或少聽說過裝飾器,這是一個(gè) python 的語法糖我們可以用它來做很多有趣的事情,比如在不修改源代碼的基礎(chǔ)之上給函數(shù)附加一些功能,比如說計(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é)構(gòu),方便我們對(duì)上面函數(shù)執(zhí)行的過程進(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é)碼則是在先前壓入??臻g當(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)行??臻g當(dāng)中。

接下來的三條字節(jié)碼 LOAD_CONST,LOAD_CONST 和 MAKE_FUNCTION,在執(zhí)行這三條字節(jié)碼之后,??臻g如下所示:

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

接下來便是 STORE_NAME 字節(jié)碼,這條字節(jié)碼的含義我們在前面已經(jīng)說過了,就是將棧頂元素彈出,保存到 co_names[oparg] 指向的對(duì)象當(dāng)中,在上面的代碼當(dāng)中也就是將棧頂?shù)膶?duì)象保存到 fib 當(dāng)中。棧頂元素 fib 函數(shù)是調(diào)用函數(shù) decorator 的返回值。
看到這里就能夠理解了原來裝飾器的最根本的原理不就是函數(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ì)保存,我們可以使用下面的代碼來驗(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+Pygame實(shí)現(xiàn)變異狗大戰(zhàn)游戲
只有你想不到,沒有我找不到寫不了的好游戲!這篇文章就來和大家分享一下如何基于Python+Pygame實(shí)現(xiàn)變異狗大戰(zhàn)游戲,感興趣的可以了解一下2023-03-03
django配置使用asgi的實(shí)現(xiàn)步驟
本文主要介紹了django配置使用asgi的實(shí)現(xiàn)步驟,支持 ASGI 協(xié)議能處理傳統(tǒng)HTTP請(qǐng)求,也能支持實(shí)時(shí)WebSocket通信,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
python3新特性函數(shù)注釋Function Annotations用法分析
這篇文章主要介紹了python3新特性函數(shù)注釋Function Annotations用法,結(jié)合實(shí)例形式分析了Python3函數(shù)注釋的定義方法與使用技巧,需要的朋友可以參考下2016-07-07
2020年10款優(yōu)秀的Python第三方庫,看看有你中意的嗎?
2020已經(jīng)過去,在過去的一年里,又有非常多優(yōu)秀的Python庫涌現(xiàn)出來。相對(duì)于numpy、TensorFlow、pandas這些已經(jīng)經(jīng)過多年維護(hù)、迭代,對(duì)于大多數(shù)Python開發(fā)者耳熟能詳?shù)膸觳煌?/div> 2021-01-01
使用Python實(shí)現(xiàn)Office文檔(Word/Excel/PowerPoint)批量轉(zhuǎn)換為PDF
在處理不同格式的Office文檔(如Word、Excel和PowerPoint)時(shí),將其轉(zhuǎn)換為PDF格式是常見的需求,本文就跟隨小編來看看如何使用Python將Word/Excel/PowerPoint批量轉(zhuǎn)換為PDF吧2024-10-10
如何將yolo格式轉(zhuǎn)化為voc格式:txt轉(zhuǎn)xml(親測有效)
這篇文章主要介紹了如何將yolo格式轉(zhuǎn)化為voc格式:txt轉(zhuǎn)xml,親測有效,可以使用,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),感興趣的朋友參考下吧2023-12-12最新評(píng)論

