Python執行機制以及pyc/pyo檔案的作用
作為Python愛好者,需要了解.py指令碼的基本執行機制及特性:
在很多工作上Python的執行流程基本上取決於使用者,因此原始碼不需要編譯成二進位制程式碼(否則無法實現大部分貼近使用者的特性),而直接從原始碼執行程式。當我們執行python檔案程式的時候,Python直譯器將原始碼轉換為位元組碼,然後再由直譯器來執行這些位元組碼。因此總的來說,它具有以下三條特性
- 原始碼距離底層更遠(根據官方文件的解釋。不說,你們也感覺得到)(。・`ω´・)
- 執行時都需要生成位元組碼,交由虛擬機器執行。(你們問我虛擬機器在哪兒?!你們也不看看各自都是用什麼軟體執行的!沒錯,就是直譯器,別和我說是IDLE啊。虛擬機器具體實現了由switch-case語句構成的框架函式PyEval_EvalFrameEx,剛剛說的位元組碼就是這貨執行的)
- 每次執行指令碼,虛擬機器總要多出載入和連結的流程。(所以呢,相比於編譯型語言就有點慢了。這與“有絲分裂間期”一樣,準備東西也要花時間啊!)
那麼,有人要問了:“不是說,執行時總要生成位元組碼麼!那,位元組碼都去哪兒了?” 咳咳,別急!容我先說說,虛擬機器它是怎麼執行指令碼的(咕嚕咕嚕喝杯水…):
- 完成模組的載入和連結;
- 將原始碼翻譯為PyCodeObject物件(這貨就是位元組碼),並將其寫入記憶體當中(方便CPU讀取,起到加速程式執行的作用);
- 從上述記憶體空間中讀取指令並執行;
- 程式結束後,根據命令列呼叫情況(即執行程式的方式)決定是否將PyCodeObject寫回硬碟當中(也就是直接複製到.pyc或.pyo檔案中);
- 之後若再次執行該指令碼,則先檢查本地是否有上述位元組碼檔案。有則執行,否則重複上述步驟。
你看!在我們點選(或輸入命令)執行指令碼,並悠閒地喝咖啡時,“人家”虛擬機器做了這麼多的事情。不過,你有沒有發現.pyc或.pyo檔案是否生成,是取決於我們如何執行程式的(雖然我們不知道要怎麼做(ง •̀_•́)ง )。
同樣,有人會吐槽:“哼!為什麼不直接生成這些檔案,這樣來得不是‘更快、更高、更強’!”
其實,虛擬機器也是講究效率的。畢竟對於比較大的專案,要將PyCodeObject寫回硬碟也是不可避免地要花些時間的,而且它又不知道你是不是也就只執行一次,之後就對剛剛跑完的指令碼“棄之不顧”了呢。不過,它其實也有貼心的一面。比如,
- 若你在命令列直接輸入“python path/to/projectDir”(假設projectDir目錄含有“main.py”檔案,以及其他將要呼叫的模組),那麼程式執行結束後便自動為當前目錄下所有的指令碼生成位元組碼檔案,並保存於本地新資料夾pycache當中。(這也有可能是IDE寫小專案時自動生成.pyc檔案的原因,不過問題描述略微曖昧。詳情參見上面知乎問題板塊)
或者是,在命令列輸入“python path/to/projectDir/main.py”,則生成除main.py外指令碼的位元組碼檔案。不過總的來說,上述這兩種行為都大大縮短了專案執行前的準備時間(畢竟分工明確的程式,規模應該不會太小,複用率也不會太低。除非吃飽了撐著,搞出這麼多事情(Θ皿Θメ))
- 模組在每次匯入前總會檢查其位元組碼檔案的修改時間是否與自身的一致。若是則直接從該位元組碼檔案讀取內容,否則源模組重新匯入,並在最後生成同名檔案覆蓋當前已有的位元組碼,從而完成內容的更新(詳見import.py)。這樣,就避免了修改原始碼後與本地位元組碼檔案產生衝突(當然,設計者也不會這麼傻。╮( ̄▽ ̄”)╭)。
若想優化生成位元組碼,應注意這兩點:
- .pyc檔案是由.py檔案經過編譯後生成的位元組碼檔案,其載入速度相對於之前的.py檔案有所提高,而且還可以實現原始碼隱藏,以及一定程度上的反編譯。比如,Python3.3編譯生成的.pyc檔案,Python3.4就別想著去執行啦!→_→.
- pyo檔案也是優化(注意這兩個字,便於後續的理解)編譯後的程式(相比於.pyc檔案更小),也可以提高載入速度。但對於嵌入式系統,它可將所需模組編譯成.pyo檔案以減少容量。
但總的來說,作用上是幾乎與原來的.py指令碼沒有區別的,也就是“然並卵 ”(當然,並非毫無作用。比如,我個人覺得用處最大的地方就是防止別人偷看我的程式碼。(:з」∠)畢竟.py原始檔是直接以原始碼的形式呈現給大家的)。╮(╯▽╰)╭ 呃…這麼說,好像又有點自相矛盾的趕腳。
在所有的Python選項中:
- -O,表示優化生成.pyo位元組碼(這裡又有“優化”兩個字,得注意啦!)
- -OO,表示進一步移除-O選項生成的位元組碼檔案中的文件字串(這是在作用效果上解釋的,而不是說從-O選項得到的檔案去除)
- -m,表示匯入並執行指定的模組
對此,我們可以使用如下格式執行.py檔案來生成.pyc檔案(以下呼叫均假設/path/to目錄含有.py指令碼):
python -m py_compile /path/to/需要生成.pyc的指令碼.py #若批量處理.py檔案
#則替換為/path/to/{需要生成.pyc的指令碼1,指令碼2,...}.py
#或者/path/to/
其效果等效於如下程式碼:
import py_compile
py_compile.compile(r'/path/to/需要生成.pyc的指令碼.py') #同樣也可以是包含.py檔案的目錄路徑
#此處儘可能使用raw字串,從而避免轉義的麻煩。比如,這裡不加“r”的話,你就得對斜槓進行轉義
py_compile是Python的自帶模組,這裡面就兩個函式(看到這個,我笑了(๑•́ ₃ •̀๑)噗噗)。其下的py_compile.compile(file[, cfile[, dfile[, doraise]]])可將.py檔案編譯生成.pyc檔案(預設),對應的引數解釋如下
- file,表示需要生成.pyc或.pyo檔案的源指令碼名(字串);
- cfile,表示需要生成.pyc或.pyo檔案的目標指令碼名。呃…好像沒有區別(>﹏<) ,也就是源指令碼—–[巴拉拉賜予你力量!編譯!]( * ̄▽ ̄)o ─═≡※:☆—–>目標指令碼。當然,它預設是以.pyc為副檔名的路徑名的字串(呼…好長)。此外,當且僅當所使用的直譯器允許編譯成.pyo檔案,才能以“.pyo”結尾。這也就是我上面為什麼會在函式功能解釋上加上“(預設)”這兩個字的原因。
- dfile,表示編譯出錯時,將報錯資訊中的名字“file”替換為“dfile”。
- doraise,設定是否忽略異常。若為True,則丟擲PyCompileError異常;否則直接將錯誤資訊寫入sys.stderr(什麼!不知道sys.stderr?!溫馨提示:sys.stderr是Python自帶的標準錯誤輸出)
(╯’ - ‘)╯︵ ┻━┻ (掀桌子) ┬─┬ ノ( ’ - ‘ノ) (擺好擺好) (╯°Д°)╯︵ ┻━┻(再TA喵掀一次)
另外,生成.pyo檔案的格式呼叫如下:
python -O -m py_compile /path/to/需要生成.pyo的指令碼.py
那麼,有人要問了:為什麼不是像生成.pyc檔案那樣採用“python -O /path/to/需要生成.pyo的指令碼.py”形式的呼叫?
“忘記”說明這一點了,很多部落格以及書籍都像我上面那樣解釋“-O”選項的作用,但詳細來解釋的話是
-O選項,將.pyc檔案優化(注意我一直強調的“優化”二字,這裡就用到啦!)為.pyo檔案,而不是將.py檔案優化編譯為.pyo檔案。(其直接的結果是優化編譯後的檔案略微小於.pyc檔案,也就是“減肥”了。現在,大家知道.pyo檔案為什麼小的原因了吧!)
注意:
以上無論是生成.pyc還是.pyo檔案,都將在當前指令碼的目錄下生成一個含有位元組碼的資料夾pycache。
可能還有人會問,.pyd檔案又是什麼鬼(>﹏<)?!(問題真多,精分ing…) 別在意,那只是Python的動態連結庫。如果要深究,還得扯上C++的知識(長篇大論的,會被噴的啊)。
再囉嗦一句:生成位元組碼的方法多了去了,不止以上這幾種。比如,你們不妨試試將上面命令列呼叫中的“py_compile”改成“compileall”,而程式碼行中的“py_compile.compile”改成“compileall.compile_file”或“compileall.compile_dir”,又或者直接使用帶有編譯功能的IDE生成位元組碼。
再再囉嗦一句:知道Python執行機制,並不是我們一般人所必須的(吃瓜群眾:“滾!我剛好不容易看完了,你才說?!”)。但是,瞭解其加速程式執行以及優化程式碼的設計思想,對於我們在日後構造快取系統、如何減少不必要的執行時間,以及同步更新工作內容等問題上起到很大的借鑑作用。
若想要了解更多的內容,可以去翻翻官方文件和其他部落格:
貼心提示:以上顏文字以及搞怪語氣,主要是為了能夠提高各位的閱讀興趣。如有不當的地方,懇請各位能夠悉心指出。
相關推薦
Python執行機制以及pyc/pyo檔案的作用
作為Python愛好者,需要了解.py指令碼的基本執行機制及特性: 在很多工作上Python的執行流程基本上取決於使用者,因此原始碼不需要編譯成二進位制程式碼(否則無法實現大部分貼近使用者的特性),而直接從原始碼執行程式。當我們執行python檔案程式的
MapTask和ReduceTask執行機制以及Map任務的並行度
1、MapTask執行機制詳解以及Map任務的並行度 整個Map階段流程大體如下圖所示。 簡單概述:inputFile通過split被邏輯切分為多個split檔案,通過Record按行讀取內容給map(使用者自己實現的)進行處理,資料被map處理結束之後交給
maptask執行機制,與小檔案的優化合並TextInputFormat
maptask並行度與決定機制 1)一個job任務map階段並行度由客戶端提交的任務所決定 2)每一個split分配一個maptask並行處理 3)預設情況下,split大小=block
將python執行結果儲存至本地檔案中
一、建立檔案,儲存資料 1.使用python中內建的open函式 開啟txt檔案 #mode 模式 #w 只能操作寫入 r 只能讀取 a 向檔案追加 #w+ 可讀可寫 r+可讀可寫 a+可讀可追加 #wb+寫入進位制資料 #w模式開啟檔案,如果而檔案中有資料,再次寫
hibernate的事務處理機制以及flush方法的作用
關於在使用hibernate在提交事務時常遇到的異常: an assertion failure occured (this may indicate a bug in Hibernate, butis more likely due to unsafe us
ASP.NET頁面執行機制以及請求處理流程
IIS處理頁面的執行機制IIS自身是不能處理像ASPX副檔名這樣的頁面,只能直接請求像HTML這樣的靜態檔案,之所以能處理ASPX這樣副檔名的頁面,是因為IIS有一個ISAPI過濾器,它是一個COM元件。ASP.NET服務在註冊到IIS的時候,就會新增一個Win32的擴
python 執行緒中 join 方法的作用
join 方法:阻塞執行緒 , 直到該執行緒執行完畢 因此 ,可以對join加一個超時操作 , join([timeout]),超過設定時間,就不再阻塞執行緒 jion加上還有一個後果就是, 子執
vlc內部執行機制以及架構分析
vlc架構剖析 1. VideoLan簡介 1.1 videolan組成 Videolan有以下兩部分組成: VLC:一個最主要的部分,它可以播放各種型別的媒體檔案和流媒體檔案,並且可以創造媒體 流並儲存成各種格式的媒體檔案,這些檔案的質量要比沒儲存前的件好。videolan作為客戶 端可以播放本地檔案,h
JavaScript 執行機制以及Event Loop(事件迴圈)
一、JavaScript單執行緒 眾所周知JavaScript是一門單執行緒語言,也就是說,在同一時間內JS只能做一件事。為什麼JavaScript不能有多個執行緒呢?這樣不是能夠提高效率嗎? JavaScript的單執行緒,與它的用途有關。作為瀏覽器指令碼語言,JavaScript的主要用途是與使用者互
Python的執行機制--pyc檔案格式淺析
Python的原始程式碼在執行前都會被先編譯成位元組碼,並把編譯的結果儲存到一個一個的PyCodeObject中,pyc 檔案即是把PyCodeObject從記憶體中以marshal格式儲存到檔案後的結果。 下面我們來通過測試和工具來了解下pyc檔案到底有些什麼東西。
python的py、pyc、pyo、pyd檔案區別
1.如果需要特殊的單獨編譯,則只需要使用py_complie這個模組就行了,如下import py_compilepy_compile.compile(r‘H:\game\test.py‘)compile函式原型:compile(file[, cfile[, dfile[, doraise]]])file 表
python中pyc和pyo的作用
pyc檔案,是python編譯後的位元組碼(bytecode)檔案。只要你運行了py檔案,python編譯器就會自動生成一個對應的pyc位元組碼檔案。這個pyc位元組碼檔案,經過python直譯器,會生成機器碼執行(這也是為什麼pyc檔案可以跨平臺部署,類似於jav
python中多執行緒排程機制以及GIL
總結下python中執行緒排程機制. 對於執行緒排程機制而言,同作業系統的程序排程一樣,最關鍵是要解決兩個問題: 1.在何時選擇掛起當前執行緒,並選擇處於等待的先一個執行緒呢? 2.在眾多等待的執行緒中,選擇哪一個作為啟用執行緒呢? 在python多執行緒機制中,這個兩
request,response,session,application四者之間的作用域聯絡以及各自的執行機制
request:伺服器獲取使用者請求;response:伺服器答覆使用者請求的資源; session:使用者使用伺服器開始到結束的過程;application:網站伺服器啟動到停止的過程; 在asp中,作用域可分為四種: ①僅僅用於JSP中的page其作用域為在當前頁面有
反編譯python的pyc/pyo位元組碼檔案
github專案:點選開啟連結 執行環境需要python2.7,可以反編譯2.5-2.7的python位元組碼。 下載專案後執行python setup.py install即可(執行該命令輸出的內容
python裝飾器執行機制
abc print 前沿 spl ini self. color ret instance 前沿: 首先是看到了單例模型,想不明白 outer中的參數 為什麽能像 global的參數 一樣屹立不倒。 #單例模型 def single_model(cls): in
python獲取一個目錄下的檔案以及子檔案中的檔案
Python獲取資料夾下的檔案和子資料夾 這裡使用os.walk函式 關於函式該函式的介紹如下 os.walk(top, topdown=Ture, onerror=None, followlinks=False) 引數: top – 根目錄下的每一個資料夾(包含它自
nodejs連線Access資料庫,出現Error: Spawn C:\WINDOWS\SysWOW64\cscript.exe error Java專案生成可執行jar包、exe檔案以及在Windows下的安裝檔案
因為集成了第三方的資料庫,所以需要獲取資料庫的資料 使用 var connection = ADODB.open('Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+config.id_path+';');  
Python程序,執行緒以及協程對比
執行緒:一份程式碼資源有多個箭頭同時執行 程序:拷貝多份程式碼資源中只存在一個箭頭(執行緒)來執行 協程 : 利用程式的阻塞或者暫停時間完成多工 即:資源分配的程式碼是程序,而執行程式碼的是執行緒, 可以理解為程序是工廠的一條流水線,其中執行緒就是流水線上的工人。 協程利用閒散時間加班的有志
python執行scp命令拷貝檔案及資料夾到遠端主機的目錄
系統環境centos7 python2.7 先在作業系統安裝expect [[email protected] python]# vi 3s.py #!/usr/bin/python #coding:utf-8 import sys,re import os import