您好,登錄后才能下訂單哦!
這篇文章主要介紹“Python裝飾器如何實現(xiàn)”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Python裝飾器如何實現(xiàn)”文章能幫助大家解決問題。
這個指令用于將一個常量加載到棧中。常量可以是數(shù)字、字符串、元組、列表、字典等對象。例如:
>>> dis.dis(lambda: 42) 1 0 LOAD_CONST 1 (42) 2 RETURN_VALUE
這個指令用于將一個變量加載到棧中。例如:
>>> dis.dis(lambda: x) 1 0 LOAD_GLOBAL 0 (x) 2 RETURN_VALUE >>>
這個指令用于將棧頂?shù)闹荡鎯Φ揭粋€變量中。例如:
>>> dis.dis("x=42") 1 0 LOAD_CONST 0 (42) 2 STORE_NAME 0 (x) 4 LOAD_CONST 1 (None) 6 RETURN_VALUE
這個指令用于對棧頂?shù)膬蓚€值進行加法運算并將結果推送到棧中。
>>> dis.dis(lambda: x + y) 1 0 LOAD_GLOBAL 0 (x) 2 LOAD_GLOBAL 1 (y) 4 BINARY_ADD 6 RETURN_VALUE
這個指令用于對棧頂?shù)膬蓚€值進行減法運算并將結果推送到棧中。
>>> 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
這個指令用于比較棧頂?shù)膬蓚€值,并且將比較得到的結果壓入棧中,這個字節(jié)碼后面后一個字節(jié)的參數(shù),表示小于大于不等于等等比較符號。例如:
>>> 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
將棧頂元素彈出作為返回值。
這個指令用于創(chuàng)建一個列表。例如:
>>> 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é)碼指令有一個參數(shù)表示??臻g當中列表元素的個數(shù),在上面的例子當中這個參數(shù)是 4 。
這個指令用于創(chuàng)建一個元組。例如:
>>> 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
同樣的這個字節(jié)碼也有一個參數(shù),表示創(chuàng)建元組的元素個數(shù)。
這個指令用于創(chuàng)建一個字典。例如:
和 list 和 tuple 一樣,這條指令是用于創(chuàng)建一個集合對象,同樣的這條指令也有一個參數(shù)表示用于創(chuàng)建集合的元素的個數(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
這條指令是用于創(chuàng)建一個字典對象,同樣的這條指令也有一個參數(shù),表示字典當中元素的個數(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
如果你是一個 pythoner 那么你肯定或多或少聽說過裝飾器,這是一個 python 的語法糖我們可以用它來做很多有趣的事情,比如在不修改源代碼的基礎之上給函數(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
在上面的代碼當中我們實現(xiàn)了一個計算斐波拉契數(shù)列的函數(shù),除此之外還寫了一個 eval_time 函數(shù)用于計算函數(shù)執(zhí)行的時間,現(xiàn)在調(diào)用函數(shù) fib(10),程序的輸出如下所示:
>>>fib(10)
(55, 5.9604644775390625e-06)
可以看到實現(xiàn)了我們想要的效果。
現(xiàn)在我們使用一個更加簡單的例子來模擬上面的代碼結構,方便我們對上面函數(shù)執(zhí)行的過程進行分析:
s = """ def decorator(func): print("Hello") return func @decorator def fib(n): pass """ dis.dis(s)
上面的 dis 函數(shù)的輸出對應代碼的字節(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,這條指令主要是加載一個 code object 對象,這個對象里面主要是包含函數(shù) decorator 的字節(jié)碼,主要是上面字節(jié)碼的第二塊內(nèi)容。在執(zhí)行完這條字節(jié)碼之后??臻g如下所示:
執(zhí)行完第二條指令 LOAD_CONST 之后,會將字符串 decorator 加載進入??臻g當中。
執(zhí)行第三條指令 MAKE_FUNCTION,這條字節(jié)碼的作用是在虛擬機內(nèi)部創(chuàng)建一個函數(shù),函數(shù)的名稱為 decorator,函數(shù)對應的字節(jié)碼則是在先前壓入??臻g當中的 code object 對象,這條指令還會將創(chuàng)建好的函數(shù)對象壓入棧中。
STORE_NAME,條字節(jié)碼會將棧頂?shù)脑貜棾?,并且?co_names[oparg] 指向這個對象,在上面的字節(jié)碼當中 co_names[oparg] 就是 decorator 。
LOAD_NAME,這條字節(jié)碼就是將 co_names[oparg] 對應的名字指向的對象重新加載進入棧空間當中,也就是上面的 decorator 函數(shù)加入進行棧空間當中。
接下來的三條字節(jié)碼 LOAD_CONST,LOAD_CONST 和 MAKE_FUNCTION,在執(zhí)行這三條字節(jié)碼之后,??臻g如下所示:
接下來的一條指令非常重要,這條指令便是裝飾器的核心原理,CALL_FUNCTION 這條指令有一個參數(shù) i,在上面的字節(jié)碼當中為 1,也就是說從棧頂開始的前 i 個元素都是函數(shù)參數(shù),調(diào)用的函數(shù)在??臻g的位置為 i + 1 (從棧頂往下數(shù)),那么在上面的情況下就是說調(diào)用 decorator 函數(shù),并且將 fib 函數(shù)作為 decorator 函數(shù)的參數(shù),decorator 函數(shù)的返回值再壓入棧頂。在上面的代碼當中 decorator 函數(shù)返回值也是一個函數(shù),也就是 decorator 函數(shù)的參數(shù),即 fib 函數(shù)。
接下來便是 STORE_NAME 字節(jié)碼,這條字節(jié)碼的含義我們在前面已經(jīng)說過了,就是將棧頂元素彈出,保存到 co_names[oparg] 指向的對象當中,在上面的代碼當中也就是將棧頂?shù)膶ο蟊4娴?fib 當中。棧頂元素 fib 函數(shù)是調(diào)用函數(shù) decorator 的返回值。
看到這里就能夠理解了原來裝飾器的最根本的原理不就是函數(shù)調(diào)用嘛,比如我們最前面的用于計算函數(shù)執(zhí)行時間的裝飾器的原理就是:
fib = eval_time(fib)
將 fib 函數(shù)作為 eval_time 函數(shù)的參數(shù),再將這個函數(shù)的返回值保存到 fib 當中,當然這個對象必須是可調(diào)用的,不然后面使用 fib() 就會保存,我們可以使用下面的代碼來驗證這個效果。
def decorator(func): return func() @decorator def demo(): return "function demo return string : Demo" print(demo)
執(zhí)行上面的程序結果為:
function demo return string : Demo
可以看到 demo 已經(jīng)變成了一個字符串對象而不再是一個函數(shù)了,因為 demo = decorator(demo),而在函數(shù) decorator 當中返回值是 demo 函數(shù)自己的返回值,因此才打印了字符串。
關于“Python裝飾器如何實現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識,可以關注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。