自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

字節(jié)碼:分析 Python 執(zhí)行的終極利器

開發(fā) 后端
Code Object(代碼對象)封裝了 Python 虛擬機的字節(jié)碼和虛擬機執(zhí)行相關信息,可以把字節(jié)碼稱為 Python 虛擬機上的匯編語言。

 [[402321]]

本文轉載自微信公眾號「Python中文社區(qū)」,作者鞏慶奎。轉載本文請聯(lián)系Python中文社區(qū)公眾號。

一、什么是代碼對象

Code Object(代碼對象)封裝了 Python 虛擬機的字節(jié)碼和虛擬機執(zhí)行相關信息,可以把字節(jié)碼稱為 Python 虛擬機上的匯編語言。

學代碼對象有什么用呢?從其定義可知,字節(jié)碼是編譯后的 Python 代碼,學習代碼對象有助于我們理解 Python 虛擬機、編譯過程、執(zhí)行過程,更加深刻理解 Python 語言特性和疑難點。在解決一些疑難雜癥時,查看代碼對象的字節(jié)碼往往有事半功倍的效果。

二、探索代碼對象

Python 程序由代碼塊組成,代碼塊可以是模塊、函數(shù)或類,也可以是腳本文件,還可以是python - c 'string'和exec ('string')、eval ('string')中字符串的內容。

兩種方法:

  • fun.__code__ 獲取函數(shù) fun 主體的代碼對象
  • compile('source code','','exec')獲取代碼塊 source code 的代碼對象

三、一個函數(shù)及其代碼對象

我們定義一個函數(shù)fun(a,b),它完成簡單的加法運算。

  1. def fun(a,b): 
  2.     return a+b  

來看它的代碼對象屬性情況,我們重點關注以 co_ 開頭的屬性。

  1. for attr in dir(fun.fun.__code__): 
  2.     if attr.startswith('co_'): 
  3.         print("{attr}:\t{attrs}".format( 
  4.             attrs=getattr(attr=attr,fun.fun.__code__, attr))) 

輸出為:

  1. co_argcount: 2 
  2. co_cellvars: () 
  3. co_code: b'|\x00|\x01\x17\x00S\x00' 
  4. co_consts: (None,) 
  5. co_filename: G:\pythonCodeStudy\manuscript\0 bytecode\fun.py 
  6. co_firstlineno: 1 
  7. co_flags: 67 
  8. co_freevars: () 
  9. co_kwonlyargcount: 0 
  10. co_lnotab: b'\x00\x01' 
  11. co_name: fun 
  12. co_names: () 
  13. co_nlocals: 2 
  14. co_posonlyargcount: 0 
  15. co_stacksize: 2 
  16. co_varnames: ('a''b'

fun 函數(shù)主體的代碼對象的屬性意義如下:

  • co_argcount 函數(shù)形式參數(shù)個數(shù),這個只有函數(shù)類型代碼塊的代碼對象有,其它類型代碼塊沒有該屬性。
  • co_code 字節(jié)碼指令序列,字節(jié)碼都由操作碼 opcode 和參數(shù) opatg 組成的序列。
  • co_const 常量列表,列表內容包括如下:
    • None 函數(shù)返回值,系統(tǒng)自帶。
    • 從前往后數(shù)所有字面常量:數(shù)字和字符串。
    • 內嵌函數(shù)代碼對象 code object。
    • 內嵌函數(shù)的 qual_name 常量,如:outer..inner。
  • co_name 本函數(shù)的名字。
  • co_varnames 本函數(shù) 局部變量 ,不含被引用自由變量,包含形式參數(shù)和內嵌函數(shù)名。
  • co_names 本函數(shù)用到的非局部變量,也就是 全局變量、系統(tǒng)內置變量。
  • co_nlocals 本函數(shù)的局部變量個數(shù)。
  • co_cellvars cell 變量。
  • co_freevars 自由變量。
  • co_flag 代碼對象的種類,比如協(xié)程、生成器等,其意義定義在 include/code.h 中。
  • co_lnotab 計算字節(jié)碼偏移量代表的源代碼行號的字節(jié)序列。兩個字節(jié)序列為一個單位,指示兩條源代碼指令編譯成的字節(jié)碼之間偏移多少。
  • co_stacksize 執(zhí)行字節(jié)碼指令時,計算棧上最大的項目數(shù),和函數(shù)參數(shù)個數(shù)有關。

Python虛擬機是基于棧的機器,每步函數(shù)調用產生棧幀(stack frame)。每個棧幀包含計算棧和塊棧。所有參數(shù)壓入計算棧,調用時彈棧,計算后彈出結果,結束本棧幀。

上例中,我們沒有詳細解釋 co_cellvars 和 co_freevars,下面詳細解釋。

3.1 co_cellvars 和 co_freevars

co_cellvars 和 co_freevars 是一個相對的概念。我們寫一個嵌套函數(shù)來解釋這兩個概念:函數(shù) outer 中,定義了一個變量 e,被內部嵌套函數(shù) inner 引用;inner 函數(shù)內部使用了變量 e,但是并未在該塊內定義之。

  1. def outer(o1, o2='o2'): 
  2.     e = 'enclose' 
  3.  
  4.     def inner(i1, i2='i2'): 
  5.         print(e) 
  6.         return e 
  7.     return inner 
  8. print(outer.__code__.co_cellvars) 
  9. print(outer('i1').__code__.co_freevars) 

結果。

  1. ('e',) 
  2. ('e',) 

故此,我們知道:

  • co_cellvars 是被內部嵌套塊(函數(shù))引用的變量組成的元組。外部函數(shù)創(chuàng)建特殊的 cell 對象存儲該變量,cell 對象的生存周期超過了定義它的外部函數(shù)。這句話理解就是:外部變量執(zhí)行完之后,清理現(xiàn)場,它的變量都消失了,但 cell 對象不消失,仍然存在。這也就是所謂 * cell 變量*
  • co_freevars 就塊內使用,但是并未在該塊內定義的變量(不含全局變量、內置變量)。也就是當前塊(函數(shù)) 引用的外部 cell 變量 組成的元組,和上個 co_cellvars 是相對的概念

這兩個是一體兩面的變量。

  • 外部變量 outer 作用域里,創(chuàng)建了變量 e 以及變量 inner (函數(shù))。
  • 因為嵌套函數(shù) inner 使用了外部變量 e ,所以在 outer 函數(shù)里, e 是作為 cell 變量,綁定到特殊的 cell 對象里。
  • 這個特殊的 cell 對象和 inner 綁定在了一起,這樣 outer 作用域消失的時候, inner 內部借由 cell 對象訪問到了 e 這個變量,它對 inner 函數(shù)來說是(來自 cell 對象的)自由變量。

掌握自由變量的概念,是編寫帶狀態(tài)函數(shù)、裝飾器的基礎。

四、字節(jié)碼細節(jié)

字節(jié)碼看起來就像亂碼,如上文 fun 函數(shù)的字節(jié)碼:co_code : b '|\ x00 |\ x01 \ x17 \ x00S \ x00 '。

根據上文,字節(jié)碼由一位操作碼和一位參數(shù)組成的序列。讓我們分析細節(jié)。

co_code [0]表示第一個操作碼|,這是 ASCII 碼 124 表示的字符,在 include/opcode.h 中,可以看到 124 是 LOAD_FAST 操作碼,這是對局部變量列表進行的加載操作。其它類似的:比如 LOAD_CONST 就是對字面常量列表操作,LOAD_GLOBAL 是對全局變量操作。

如果操作碼不帶參數(shù),參數(shù)可以省略。這里的第一個操作碼的參數(shù)是 co_code[1] 為 0x00 。

因此,這個完整的字節(jié)碼操作是把局部變量列表 co_varnames 的第 0x00 索引內容 a ,壓入計算棧棧頂。

字節(jié)序列看起來比較費勁,讓我們用 dis.dis(fun)來反匯編代碼,得到字節(jié)碼如下。

  1. 2           0 LOAD_FAST                0 (a) 
  2.             2 LOAD_FAST                1 (b) 
  3.             4 BINARY_ADD 
  4.             6 RETURN_VALUE 

這樣看起來就很簡單了。

  • 第一列 2 表示源代碼的第二行,也就是return a+b這一條。
  • 第二列的 0 表示該條操作碼相對字節(jié)碼開頭的偏移量,LOAD_FAST 表示操作碼,0 表示參數(shù),(a)是由 dis 生成的,即局部變量元組第 0 個元素 a。本條將變量 a 壓入計算棧。
  • 以此類推第二條,將局部變量 b 壓入計算棧。
  • 第三條 BINARY_ADD 沒有參數(shù),它是求和,將 a 和 b 彈出,求和,結果壓入計算棧棧頂。
  • 第四條 RETURN_VALUE 彈出結果,結束本棧幀。

Python 編譯產生 pyc 文件:Python3 之前是在本地目錄產生,之后是在 pycache 目錄下。我們打開上例產生的 pyc 文件,使用十六進制查看,能明顯發(fā)現(xiàn),編譯的字節(jié)碼就直接在 PYC 文件里。

五、其它代碼塊代碼對象

上例中,我們對函數(shù)進行反匯編,使用的是 dis.dis(fun)指令,這里的 fun 是函數(shù)。

第二部分說過得到代碼對象有兩種方法,當我們對其它代碼對象進行 compile 時,實際是對該模塊進行反編譯。如果此時該代碼塊里有函數(shù),只會產生代碼對象,不會產生真正的函數(shù)對象。

比如如下語句:print(dis.dis(compile('def fun(a,b): return a+b', '', 'exec'))),輸出如下??梢姶藭r的函數(shù)只是一個代碼對象,作為常量載入,MAKE_FUNCTION 后,賦值給 fun 局部變量。

  1. 1           0 LOAD_CONST               0 (<code object fun at 0x00A67B18, file 
  2. ", line 1>) 
  3.             2 LOAD_CONST               1 ('fun'
  4.             4 MAKE_FUNCTION            0 
  5.             6 STORE_NAME               0 (fun) 
  6.             8 LOAD_CONST               2 (None) 
  7.            10 RETURN_VALUE 

六、總結

代碼對象封裝了 Python 虛擬機的字節(jié)碼和其它編譯相關信息,可以把字節(jié)碼稱為 Python 虛擬機上的匯編語言。我們分析了自由變量和 cell 變量,掌握自由變量的概念,這是編寫裝飾器、帶狀態(tài)函數(shù)的基礎。字節(jié)碼由一位操作碼和一位參數(shù)組成的序列,學習其細節(jié),有助于我們理解 Python 的特性??稍诜治鲎兞孔饔糜?、閉包時作為強大的工具。

作者:鞏慶奎,大奎,對計算機、電子信息工程感興趣。gongqingkui at 126.com

 

責任編輯:武曉燕 來源: Python中文社區(qū)
相關推薦

2024-04-12 07:50:40

Python監(jiān)控利器Time 模塊

2024-03-14 08:19:14

PythonXmltodict第三方庫

2018-04-04 15:05:17

虛擬機字節(jié)碼引擎

2015-07-08 14:56:26

2024-10-21 10:45:52

2024-10-29 10:54:07

2023-11-09 12:59:00

微力同步數(shù)據傳輸工具

2019-10-30 08:45:21

JS代碼NodeJS

2024-03-08 08:38:19

PythonJavaScriptexecjs庫

2011-12-01 14:56:30

Java字節(jié)碼

2024-04-30 09:33:00

JavaScriptPythonexecjs

2024-10-20 13:28:47

虛擬機字節(jié)碼CPU

2021-09-10 14:05:14

預測分析大數(shù)據分析大數(shù)據

2022-03-30 10:10:17

字節(jié)碼??臻g

2010-03-22 12:40:48

Python代碼加密

2021-12-09 22:36:30

Java 字節(jié)碼頁緩存

2010-09-25 10:20:05

JAVA字節(jié)碼

2013-09-05 09:37:49

2025-02-19 07:49:36

2024-07-30 14:01:51

Java字節(jié)碼JVM?
點贊
收藏

51CTO技術棧公眾號