Python 是直譯語言?其實你每天都在編譯它
Python 是直譯語言?其實你每天都在編譯它
在拿 Python 和其它語言做比較時,我們常聽到:「C、C++ 是「編譯式語言(compiled language),Python 則是直譯式語言(interpreted language)。」彷彿 Python 就是直譯器打開原始碼,一行一行邊讀邊執行,中間根本沒有「編譯」這道手續。
但其實在 Python 官方 Glossary 文件中的 interpreted 詞條中都不敢把話說死:
“Python is an interpreted language, as opposed to a compiled one, though the distinction can be blurry because of the presence of the bytecode compiler.”
官方一方面說 Python 是直譯語言,另一方面卻又說:因為存在一個 bytecode 編譯器,所以這條「直譯 vs. 編譯」的界線其實是模糊的。也就是說,「Python 是純直譯語言」這個說法,並不精確。
那麼,Python 究竟是如何同時牽涉到「編譯」與「直譯」的呢?官方 Glossary 的 bytecode 詞條給了最直接的答案——它用一句話就把這兩道流程都講清楚了:
“Python source code is compiled into bytecode, the internal representation of a Python program in the CPython interpreter. … This ‘intermediate language’ is said to run on a virtual machine that executes the machine code corresponding to each bytecode.”
- 前半句說了:編譯階段是將 source code 轉換成 bytecode
- 後半句則說:直譯階段是在虛擬機(virtual machine)上執行 byteode
也就是說,實際執行 python script.py 時,原始碼並非「直接被一行一行直譯」,而是會先被編譯(compile)成bytecode(位元組碼/code object),再交給直譯器(CPython VM)逐條執行(interpret)。
這裡就冒出一個問題了:既然 Python 跟 C、C++ 都需要編譯,那為什麼 Python 會被稱作直譯式語言,而 C、C++ 則被稱作編譯式語言呢?關鍵在於編譯後得到的產物不同。C/C++ 是直接把原始碼編成 CPU 看得懂的機器碼,可以直接在 CPU 上執行;Python 編出來的則是 bytecode,得交給 CPython VM 這台虛擬機來跑。而虛擬機「跑」bytecode 的方式,則是內部維持一個大迴圈,反覆地「取出下一條 bytecode 指令、辨認它對應哪個操作,再執行該操作」,一條接一條直到整段跑完。正是這種邊讀邊執行的做法,才使得 Python 被歸類為「直譯」式語言。
bytecode 橫跨編譯與直譯兩端的這個特性,在標準函式庫的 dis 模組文件裡也看得到。這個模組專門用來反組譯(disassemble)Python 的 bytecode,它的說明在描述 CPython bytecode 時,更是把「編譯」與「直譯」兩端寫進了同一句話:
“The CPython bytecode … is … used by the compiler and the interpreter.”
這段話中同時出現 compiler 與 interpreter,可以作為編譯與直譯這兩道流程都同時存在的強力佐證!
結語
回到最初那句「Python 是直譯式語言」——現在我們可以說得更精確:Python 不單是直譯式語言,它同時也經過編譯這道手續。
這一點並非只有官方文件這樣說。Eddie Antonio Santos 在 Python is a compiled language 一文中,用了一個很有意思的手法佐證相同的結論:他刻意在原始碼裡埋下多個錯誤,藉此觀察 Python 到底是怎麼跑的——結果這些錯誤並不是照著它們在程式碼裡的順序一個一個被檢出報錯的。這證明了 Python 至少不是我們一開始想像的那種「打開原始碼、一行一行邊讀邊執行」的純直譯式語言。至於他是怎麼一步步設計實驗、推導出這個結論的,就留待有興趣的讀者自行參閱原文一探究竟。
那麼,這道一路被我們提及、卻還沒真正拆開來看的「編譯」手續,內部到底經歷了哪些步驟?根據 Compiler design 這篇文章,Python 的編譯流程會依序經過 tokenize → parse → AST → control flow graph → bytecode,一路將 source code 轉換成 bytecode;而到了執行階段,則交由 Python/ceval.c 裡的求值迴圈(evaluation loop)逐條執行。至於每一步的細節,就留待下一篇深入查看。
更多推荐

所有评论(0)