由Python歷史 解密 Python底層邏輯

2021-10-12 07:43:54 字數 4158 閱讀 3262

python的作者,guido von rossum,荷蘭人。2023年,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語言中增加功能,比如對圖形化的支援,就必須改動很多地方。

不能直接進行io:abc語言不能直接操作檔案系統。儘管你可以通過諸如文字流的方式匯入資料,但abc無法直接讀寫檔案。

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

abc的前車之鑑,給guido帶來啟示。

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

1991 年,第乙個python 直譯器誕生,它是用 c 語言實現的,並能夠呼叫 c 語言的庫檔案。從一出生,python已經具有了:類,函式,異常處理,包含表和詞典在內的核心資料型別,以及模組為基礎的拓展系統。

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

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

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

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

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

python是一種解釋型語言,它的源**不需要編譯,可以直接從源**執行程式。python直譯器將源**轉換為位元組碼,然後把編譯好的位元組碼**到python虛擬機器(python virtual machine,pvm)中執行。

當我們執行python**的時候,在python直譯器用四個過程「拆解」我們的**:

簡單的說它就是乙個從源**編譯而來的中間檔案(用於不同作業系統平台的直譯器執行)。比如,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並不會生成:

☞拓展閱讀:

(下文詳細說明python的工作機制和python虛擬機器內幕)

python 位元組碼介紹

python原始碼編譯的結果就是pycodeobject,每個作用域會編譯出乙個對應的**物件,其中名為co_code的pystringobject儲存著**物件的位元組碼。

乙個python原始檔就是乙個模組。每個模組頂層的**物件通過marshal序列化之後就得到了.pyc檔案。marshal以little-endian位元組序來序列化資料。

那巢狀於頂層作用域裡面的那些作用域,例如函式、類的定義,它們對應的**物件在**?它們每乙個都乖乖的躺在上一層作用域的**物件的co_const(常量池)域裡,所以其實頂層**物件已經巢狀包含了底下其它作用域的**物件。

☞拓展閱讀:

(下文主要結合例項說明了.pyc檔案結構)

python 2.6.2的.pyc檔案格式

python檔案如果要發布的話,有時候還是難免想保護一下自己的原始碼,有些人就直接編譯成了pyc檔案,因為這樣既可以保留跨平台的特性,又可以不能直接看到**,也看到網上很多人說為了保護自己的**可以編譯成pyc檔案。

用pyc檔案可以保護python**的想法其實是不正確的,pyc檔案是可以很容易被反編譯的,比如說比較著名的uncompyle6庫(用來反編譯檔案最爽不過了,幾乎支援python全版本的pyc檔案的反編譯。

一般來說,**分析重要性的判斷比較主觀,不同的人有不同的認識。python是用c來實現的,所以對於python的效能或**質量的評估可以通過dis模組獲取到對應的位元組碼指令來進行評估。

一般來說乙個python語句會對應若干位元組碼指令,python的位元組碼是一種類似彙編指令的中間語言,但是乙個位元組碼指令並不是對應乙個機器指令(二進位制指令),而是對應一段c**,而不同的指令的效能不同,所以不能單獨通過指令數量來判斷**的效能,而是要通過檢視呼叫比較頻繁的指令的**來確認一段程式的效能。

乙個python的程式會有若干**塊組成,例如乙個python檔案會是乙個**塊,乙個類,乙個函式都是乙個**塊,乙個**塊會對應乙個執行的上下文環境以及一系列的位元組碼指令。

dis模組主要是用來分析位元組碼的乙個內建模組。dis 模組的文件 可以讓你遍歷它的內容,並且提供乙個位元組碼指令能夠做什麼和有什麼樣的引數的完整清單。

☞拓展閱讀:

(下文主要說明了dis模組的使用)

Python 發展歷史

python 是由 guido van rossum 在八十年代末和九十年代初,在荷蘭國家數學和電腦科學研究所設計出來的。python 本身也是由諸多其他語言發展而來的,這包括 abc modula 3 c c algol 68 smalltalk unix shell 和其他的指令碼語言等等。像 ...

Python 加密解密

coding utf 8 import hashlib 加密解密 md5是最常見的摘要演算法,速度很快,生成結果是固定的128 bit位元組,通常用乙個32位的16進製制字串表示。md5 hashlib.md5 update md5.update hhhhhaaa print md5.hexdige...

python實現約瑟夫 約瑟夫問題python實現

python語言之如何實現約瑟夫環問題 def josephus n,m if type n 60個人從1開始編號每人拿乙個號碼牌排成圈,從 1開始報數,第一次報數 totalnum 猴子總數 startnum 開始序號 intervalnum 間隔數def kingelect totalnum,s...