當前位置:
首頁 > 知識 > 起底 Python 的底層邏輯

起底 Python 的底層邏輯

本文配圖來自美劇《我們竊取秘密:維基解密的故事》。

文 | 圖靈社群

推薦 | 編程派公眾號(ID:codingpy)

01

一次純粹的hacking

Python的作者,Guido von Rossum,荷蘭人。1982年,Guido從阿姆斯特丹大學獲得了數學和計算機碩士學位。儘管,他算得上是一位數學家,但他更加享受計算機帶來的樂趣,熱衷於做任何和編程相關的活兒。

80年代,掀起了個人電腦浪潮,但受限於個人電腦配置低,所有的編譯器的核心是做優化,以便讓程序能夠運行。在那個時代,程序員恨不得用手榨取計算機每一寸的能力。有人甚至認為C語言的指針是在浪費內存,至於動態類型,內存自動管理,面向對象…… 別想了,那會讓你的電腦陷入癱瘓。

而這種編程方式讓Guido感到苦惱。Guido知道如何用C語言寫出一個功能,但整個編寫過程需要耗費大量的時間。

不過,他還有另一個選擇shell。shell可以像膠水一樣,將UNIX下的許多功能連接在一起。UNIX的管理員們常常用shell去寫一些簡單的腳本,以進行一些系統維護的工作,比如定期備份、文件系統管理等等。然而,shell的本質是調用命令,並不能全面的調動計算機的功能

Guido希望有一種語言,這種語言能夠像C語言那樣,能夠全面調用計算機的功能介面,又可以像shell那樣輕鬆的編程。

ABC語言讓Guido看到希望。ABC是由荷蘭的數學和計算機研究所開發的,Guido在該研究所工作,並參與到ABC語言的開發。ABC 語言是一個致力於為初學者設計編程環境的長達 10 年的研究項目,與當時的大部分語言不同,ABC語言的目標是「讓用戶感覺更好」。

比如下面是一段來自Wikipedia的ABC程序,這個程序用於統計文本中出現的詞的總數:

HOW TO RETURN words document: PUT {} IN collection FOR line IN document: FOR word IN split line: IF word not.in collection: INSERT word IN collection RETURN collection

HOW TO用於定義一個函數。一個Python程序員應該很容易理解這段程序。ABC語言使用冒號和縮進來表示程序塊。行 尾沒有分號。for和if結構中也沒有括弧() 。賦值採用的是PUT,而不是更常見的等號。這些改動讓ABC程序讀起來像一段文字。

儘管ABC已經具備了良好的可讀性和易用性,但最終卻也沒能流行起來。原因在於:

硬體上的困難:ABC語言編譯器需要比較高配置的電腦才能運行,而當時電腦使用者,更多考慮程序效率,而非語言難度;

一個語言設計的致命問題:其可拓展性較差,如果想在ABC語言中增加功能,比如對圖形化的支持,就必須改動很多地方。

不能直接進行IO:ABC語言不能直接操作文件系統。儘管你可以通過諸如文本流的方式導入數據,但ABC無法直接讀寫文件。

輸入輸出的困難對於計算機語言來說是致命的。你能想像一個打不開車門的跑車么?

ABC的前車之鑒,給Guido帶來啟示。

1989年,為了打發聖誕節假期,Guido開始寫Python語言的編譯器。Python這個名字,來自Guido所摯愛的電視劇Monty Python"s Flying Circus。他希望這個新的叫做Python的語言,能符合他的理想:創造一種C和shell之間,功能全面,易學易用,可拓展的語言。Guido作為一個語言設計愛好者,已經有過設計語言的嘗試。這一次,也不過是一次純粹的hacking行為。

02

Python解釋器的誕生

1991 年,第一個Python 解釋器誕生,它是用 C 語言實現的,並能夠調用 C 語言的庫文件。從一出生,Python已經具有了:類,函數,異常處理,包含表和詞典在內的核心數據類型,以及模塊為基礎的拓展系統。

這裡需要牽扯一個「編譯器」的概念,其主要作用是便於人編寫,閱讀,維護的高級計算機語言翻譯為計算機能識別,運行的低級機器語言的程序。

編譯器翻譯語言方式有2種:編譯、解釋。

編譯型語言:需通過編譯器(compiler)將源代碼編譯成機器碼,之後才能執行的語言。

一般需經過編譯(compile)、鏈接(linker)這兩個步驟。編譯是把源代碼編譯成機器碼,鏈接是把各個模塊的機器碼和依賴庫串連起來生成可執行文件。

解釋型語言:解釋性語言的程序不需要編譯,相比編譯型語言省了道工序,解釋性語言在運行程序的時候才逐行翻譯。

Python是一種解釋型語言,它的源代碼不需要編譯,可以直接從源代碼運行程序。Python解釋器將源代碼轉換為位元組碼,然後把編譯好的位元組碼轉發到Python虛擬機(Python Virtual Machine,PVM)中執行。

當我們執行Python代碼的時候,在Python解釋器用四個過程「拆解」我們的代碼:

首先,當你把鍵入代碼交給Python處理的時候會先進行詞法分析,如果你鍵入關鍵字或者當輸入關鍵字有誤時,都會被詞法分析所觸發,不正確的代碼將不會被執行。

Python會進行語法分析,例如當"for i in test:"中,test後面的冒號如果被寫為其他符號,代碼依舊不會被執行。

進入最關鍵的過程,在執行Python前,Python會生成.pyc文件,這個文件就是位元組碼。

將編譯好的位元組碼轉發Python虛擬機中進行執行:由Python Virtual Machine(Python虛擬機)來執行這些編譯好的位元組碼。

03

什麼是位元組碼(bytecode)?

簡單的說它就是一個從源代碼編譯而來的中間文件(用於不同操作系統平台的解釋器執行)。比如,a說日語,b說中文,溝通起來不暢通,請一個翻譯,把a和b的語言都翻譯成英語,這個英語就可以理解成bytecode,一種中間語言。

bytecode的好處就是載入快,而且可以跨平台,同樣一份bytecode,只要有操作系統平台上有相應的Python解釋器,就可以執行,而不需要源代碼。不同版本的Python編譯的位元組碼是不兼容的,Python 2.6編譯的bytecode拿到Python 2.7上去執行就不行了。

如何生成位元組碼?

Python解釋器一般會自動把.py文件轉換成bytecode,然後再執行它。當你第一次把.py文件當作module導入,或者對應的.py文件比.pyc文件的修改時間還要新時,Python解釋器都會再從source code生成相應的新bytecode。這樣當你下次再次運行程序時,就會直接從bytecode運行,從而節省便宜時間。

Ps:這裡需要注意,有些情況bytecode並不會生成:

遇到目錄寫許可權的問題時。(比如你編寫代碼和運行代碼使用的具有不同許可權的用戶角色,Linux上很常見)

運行一個script並不會被當成是import操作,所以可能也不會生成bytecode。(比如:你有個一個a.py的文件,其中在a.py里,你import了b.py,那麼運行python a.py後,會生成b.pyc,而不會生成a.pyc)

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 編程派 的精彩文章:

2019年Python 開發者調查報告
十行 Python 代碼寫一個USB病毒

TAG:編程派 |