Python編譯過程和執(zhí)行原理解析
一. Python執(zhí)行原理
這里的解釋執(zhí)行是相對于編譯執(zhí)行而言的。我們都知道,使用C/C++之類的編譯性語言編寫的程序,是需要從源文件轉換成計算機使用的機器語言,經(jīng)過鏈接器鏈接之后形成了二進制的可執(zhí)行文件。運行該程序的時候,就可以把二進制程序從硬盤載入到內(nèi)存中并運行。
但是對于Python而言,Python源碼不需要編譯成二進制代碼,它可以直接從源代碼運行程序。當我們運行Python文件程序的時候,Python解釋器將源代碼轉換為字節(jié)碼,然后再由Python解釋器來執(zhí)行這些字節(jié)碼。這樣,Python就不用擔心程序的編譯,庫的鏈接加載等問題了。
對于Python解釋語言,有以下3方面的特性:
- 每次運行都要進行轉換成字節(jié)碼,然后再有虛擬機把字節(jié)碼轉換成機器語言,最后才能在硬件上運行。與編譯性語言相比,每次多出了編譯和鏈接的過程,性能肯定會受到影響。
- 由于不用關心程序的編譯和庫的鏈接等問題,開發(fā)的工作也就更加輕松啦。
- Python代碼與機器底層更遠了,Python程序更加易于移植,基本上無需改動就能在多平臺上運行。
在具體計算機上實現(xiàn)一種語言,首先要確定的是表示該語言語義解釋的虛擬計算機,一個關鍵的問題是程序執(zhí)行時的基本表示是實際計算機上的機器語言還是虛擬機的機器語言。這個問題決定了語言的實現(xiàn)。根據(jù)這個問題的回答,可以將程序設計語言劃分為兩大類:編譯型語言和解釋型語言。
- 編譯實現(xiàn)的語言,如:C、C++、Fortran、Pascal、Ada。由編譯型語言編寫的源程序需要經(jīng)過編譯,匯編和鏈接才能輸出目標代碼,然后由機器執(zhí)行目標代碼。目標代碼是有機器指令組成,不能獨立運行,因為源程序中可能使用了一些匯編程序不能解釋引用的庫函數(shù),而庫函數(shù)又不在源程序中,此時還需要鏈接程序完成外部引用和目標模板調用的鏈接任務,最后才能輸出可執(zhí)行代碼。
- 解釋型語言,解釋器不產(chǎn)生目標機器代碼,而是產(chǎn)生中間代碼,這種中間代碼與機器代碼不同,中間代碼的解釋是由軟件支持的,不能直接使用在硬件上。該軟件解釋器通常會導致執(zhí)行效率較低,用解釋型語言編寫的程序是由另一個可以理解中間代碼的解釋程序執(zhí)行的。和編譯的程序不同的是,解釋程序的任務是逐一將源代碼的語句解釋成可執(zhí)行的機器指令,不需要將源程序翻譯成目標代碼再執(zhí)行。對于解釋型語言,需要一個專門的解釋器來執(zhí)行該程序,每條語句只有在執(zhí)行是才能被翻譯,這種解釋型語言每執(zhí)行一次就翻譯一次,因而效率低下。
- Java解釋器,java很特殊,java是需要編譯的,但是沒有直接編譯成機器語言,而是編譯成字節(jié)碼,然后在Java虛擬機上用解釋的方式執(zhí)行字節(jié)碼。Python也使用了類似的方式,先將Python編譯成Python字節(jié)碼,然后由一個專門的Python字節(jié)碼解釋器負責解釋執(zhí)行字節(jié)碼。
- Python是一門解釋語言,但是出于效率的考慮,提供了一種編譯的方法。編譯之后就得到pyc文件,存儲了字節(jié)碼。Python這點和java很類似,但是java與Python不同的是,Python是一個解釋型的語言,所以編譯字節(jié)碼不是一個強制的操作,事實上,編譯是一個自動的過程,一般不會在意它的存在。編譯成字節(jié)碼可以節(jié)省加載模塊的時間,提高效率。
- 除了效率之外,字節(jié)碼的形式也增加了反向工程的難度,可以保護源代碼。這個只是一定程度上的保護,反編譯還是可以的。
二. Python內(nèi)部執(zhí)行過程
2.1 編譯過程概述
當我們執(zhí)行Python代碼的時候,在Python解釋器用四個過程“拆解”我們的代碼,最終被CPU執(zhí)行返回給用戶。
首先當用戶鍵入代碼交給Python處理的時候會先進行詞法分析,例如用戶鍵入關鍵字或者當輸入關鍵字有誤時,都會被詞法分析所觸發(fā),不正確的代碼將不會被執(zhí)行。
下一步Python會進行語法分析,例如當"for i in test:"中,test后面的冒號如果被寫為其他符號,代碼依舊不會被執(zhí)行。
下面進入最關鍵的過程,在執(zhí)行Python前,Python會生成.pyc文件,這個文件就是字節(jié)碼,如果我們不小心修改了字節(jié)碼,Python下次重新編譯該程序時會和其上次生成的字節(jié)碼文件進行比較,如果不匹配則會將被修改過的字節(jié)碼文件進行覆蓋,以確保每次編譯后字節(jié)碼的準確性。
那么什么是字節(jié)碼?字節(jié)碼在Python虛擬機程序里對應的是PyCodeObject對象。.pyc文件是字節(jié)碼在磁盤上的表現(xiàn)形式。簡單來說就是在編譯代碼的過程中,首先會將代碼中的函數(shù)、類等對象分類處理,然后生成字節(jié)碼文件。有了字節(jié)碼文件,CPU可以直接識別字節(jié)碼文件進行處理,接著Python就可執(zhí)行了。
2.2 過程圖解
2.3 編譯字節(jié)碼
Python中有一個內(nèi)置函數(shù)compile(),可以將源文件編譯成codeobject,首先看這個函數(shù)的說明:
compile(...) compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
參數(shù)1:源文件的內(nèi)容字符串
參數(shù)2:源文件名稱
參數(shù)3:exec-編譯module,single-編譯一個聲明,eval-編譯一個表達式 一般使用前三個參數(shù)就夠了
使用示例:
#src_file.py #some function def f(d=0): c=1 print "hello" a=9 b=8 f() >>> a=open('src_file.py','r').read() #命令行模式中打開源文件進行編譯 >>> co=compile(a,'src_file','exec') >>> type(co) <type 'code'> #編譯出了codeobject對象
2.4 codeobject對象的屬性
codeobject有哪些變量,接上節(jié)的內(nèi)容分析一下:
print(co.co_names) #所有的符號名稱 # ('f', 'a', 'b') print(co.co_name)#模塊名、函數(shù)名、類名 # <module> print(co.co_consts) #常量集合、函數(shù)f和兩個int常量a,b,d # (0, <code object f at 0xb7273b18, file "src_file", line 2>, 9, 8, None) print(co.co_consts[1].co_varnames) #可以看到f函數(shù)也是一個codeobject,打印f中的局部變量 # ('c',) print(co.co_code) #字節(jié)碼指令 # dZdZdZedS print(co.co_consts[1].co_firstlineno) #代碼塊在文件中的起始行號 # 2 print(co.co_stacksize) #代碼棧大小 # 2 print(co.co_filename) #文件名 # src_file #模塊名、函數(shù)名、類名
codeobject的co_code代表了字節(jié)碼,這個字節(jié)碼有什么含義?我們可以使用dis模塊進行Python的反編譯:
import dis dis.dis(co) print(output) ''' 2 0 LOAD_CONST 0 (0) 3 LOAD_CONST 1 (<code object f at 0xb7273b18, file "src_file", line 2>) 6 MAKE_FUNCTION 1 9 STORE_NAME 0 (f) 5 12 LOAD_CONST 2 (9) 15 STORE_NAME 1 (a) 6 18 LOAD_CONST 3 (8) 21 STORE_NAME 2 (b) 7 24 LOAD_NAME 0 (f) 27 CALL_FUNCTION 0 30 POP_TOP 31 LOAD_CONST 4 (None) 34 RETURN_VALUE '''
從反編譯的結果來看,Python字節(jié)碼其實是模仿的x86的匯編,將代碼編譯成一條一條的指令交給一個虛擬的cpu去執(zhí)行。
- 第一列:行號
- 第二列:指令在代碼塊中的偏移量
- 第三列:指令
- 第四列:操作數(shù)
- 第五列:操作數(shù)說明
到此這篇關于Python編譯過程和執(zhí)行原理的文章就介紹到這了,更多相關Python執(zhí)行原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解Python的Django框架中manage命令的使用與擴展
這篇文章主要介紹了Python的Django框架中manage命令的使用與擴展,manage.py使得用戶借助manage命令在命令行中能實現(xiàn)諸多簡便的操作,需要的朋友可以參考下2016-04-04一文詳解Python中數(shù)據(jù)清洗與處理的常用方法
在數(shù)據(jù)處理與分析過程中,缺失值、重復值、異常值等問題是常見的挑戰(zhàn),本文總結了多種數(shù)據(jù)清洗與處理方法,文中的示例代碼簡潔易懂,有需要的小伙伴可以參考下2025-01-01Python實現(xiàn)簡單的ui界面的設計步驟(適合小白)
當我們書寫一個python程序時,我們在控制臺輸入信息時,往往多有不便,并且為了更加美觀且直觀的方式輸入控制命令,我們常常設計一個ui界面,這樣就能方便執(zhí)行相關功能,如計算器、日歷等界面,本博客是為了給ui設計的小白進行講解,需要的朋友可以參考下2024-07-07