python底层与机器底层关系_由Python历史「解密」Python底层逻辑
一次純粹的hacking
Python的作者,Guido von Rossum,荷蘭人。1982年,Guido從阿姆斯特丹大學(xué)獲得了數(shù)學(xué)和計(jì)算機(jī)碩士學(xué)位。盡管,他算得上是一位數(shù)學(xué)家,但他更加享受計(jì)算機(jī)帶來(lái)的樂(lè)趣,熱衷于做任何和編程相關(guān)的活兒。
80年代,掀起了個(gè)人電腦浪潮,但受限于個(gè)人電腦配置低,所有的編譯器的核心是做優(yōu)化,以便讓程序能夠運(yùn)行。在那個(gè)時(shí)代,程序員恨不得用手榨取計(jì)算機(jī)每一寸的能力。有人甚至認(rèn)為C語(yǔ)言的指針是在浪費(fèi)內(nèi)存,至于動(dòng)態(tài)類型,內(nèi)存自動(dòng)管理,面向?qū)ο蟆?別想了,那會(huì)讓你的電腦陷入癱瘓。
而這種編程方式讓Guido感到苦惱。Guido知道如何用C語(yǔ)言寫出一個(gè)功能,但整個(gè)編寫過(guò)程需要耗費(fèi)大量的時(shí)間。
不過(guò),他還有另一個(gè)選擇shell。shell可以像膠水一樣,將UNIX下的許多功能連接在一起。UNIX的管理員們常常用shell去寫一些簡(jiǎn)單的腳本,以進(jìn)行一些系統(tǒng)維護(hù)的工作,比如定期備份、文件系統(tǒng)管理等等。然而,shell的本質(zhì)是調(diào)用命令,并不能全面的調(diào)動(dòng)計(jì)算機(jī)的功能。
Guido希望有一種語(yǔ)言,這種語(yǔ)言能夠像C語(yǔ)言那樣,能夠全面調(diào)用計(jì)算機(jī)的功能接口,又可以像shell那樣輕松的編程。
**ABC語(yǔ)言讓Guido看到希望。**ABC是由荷蘭的數(shù)學(xué)和計(jì)算機(jī)研究所開發(fā)的,Guido在該研究所工作,并參與到ABC語(yǔ)言的開發(fā)。ABC 語(yǔ)言是一個(gè)致力于為初學(xué)者設(shè)計(jì)編程環(huán)境的長(zhǎng)達(dá) 10 年的研究項(xiàng)目,與當(dāng)時(shí)的大部分語(yǔ)言不同,ABC語(yǔ)言的目標(biāo)是“讓用戶感覺更好”。
比如下面是一段來(lái)自Wikipedia的ABC程序,這個(gè)程序用于統(tǒng)計(jì)文本中出現(xiàn)的詞的總數(shù):
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用于定義一個(gè)函數(shù)。一個(gè)Python程序員應(yīng)該很容易理解這段程序。ABC語(yǔ)言使用冒號(hào)和縮進(jìn)來(lái)表示程序塊。行 尾沒有分號(hào)。for和if結(jié)構(gòu)中也沒有括號(hào)() 。賦值采用的是PUT,而不是更常見的等號(hào)。這些改動(dòng)讓ABC程序讀起來(lái)像一段文字。
盡管ABC已經(jīng)具備了良好的可讀性和易用性,但最終卻也沒能流行起來(lái)。原因在于:
硬件上的困難:ABC語(yǔ)言編譯器需要比較高配置的電腦才能運(yùn)行,而當(dāng)時(shí)電腦使用者,更多考慮程序效率,而非語(yǔ)言難度;
一個(gè)語(yǔ)言設(shè)計(jì)的致命問(wèn)題:其可拓展性較差,如果想在ABC語(yǔ)言中增加功能,比如對(duì)圖形化的支持,就必須改動(dòng)很多地方。
不能直接進(jìn)行IO:ABC語(yǔ)言不能直接操作文件系統(tǒng)。盡管你可以通過(guò)諸如文本流的方式導(dǎo)入數(shù)據(jù),但ABC無(wú)法直接讀寫文件。
輸入輸出的困難對(duì)于計(jì)算機(jī)語(yǔ)言來(lái)說(shuō)是致命的。你能想像一個(gè)打不開車門的跑車么?
ABC的前車之鑒,給Guido帶來(lái)啟示。
1989年,為了打發(fā)圣誕節(jié)假期,Guido開始寫Python語(yǔ)言的編譯器。Python這個(gè)名字,來(lái)自Guido所摯愛的電視劇Monty Python's Flying Circus。他希望這個(gè)新的叫做Python的語(yǔ)言,能符合他的理想:創(chuàng)造一種C和shell之間,功能全面,易學(xué)易用,可拓展的語(yǔ)言。Guido作為一個(gè)語(yǔ)言設(shè)計(jì)愛好者,已經(jīng)有過(guò)設(shè)計(jì)語(yǔ)言的嘗試。這一次,也不過(guò)是一次純粹的hacking行為。
Python解釋器的誕生
1991 年,第一個(gè) Python 解釋器誕生,它是用 C 語(yǔ)言實(shí)現(xiàn)的,并能夠調(diào)用 C 語(yǔ)言的庫(kù)文件。從一出生,Python已經(jīng)具有了:類,函數(shù),異常處理,包含表和詞典在內(nèi)的核心數(shù)據(jù)類型,以及模塊為基礎(chǔ)的拓展系統(tǒng)。
這里需要牽扯一個(gè)“編譯器”的概念,其主要作用是便于人編寫,閱讀,維護(hù)的高級(jí)計(jì)算機(jī)語(yǔ)言翻譯為計(jì)算機(jī)能識(shí)別,運(yùn)行的低級(jí)機(jī)器語(yǔ)言的程序。
編譯器翻譯語(yǔ)言方式有2種:編譯、解釋。
①編譯型語(yǔ)言:需通過(guò)編譯器(compiler)將源代碼編譯成機(jī)器碼,之后才能執(zhí)行的語(yǔ)言。
一般需經(jīng)過(guò)編譯(compile)、鏈接(linker)這兩個(gè)步驟。編譯是把源代碼編譯成機(jī)器碼,鏈接是把各個(gè)模塊的機(jī)器碼和依賴庫(kù)串連起來(lái)生成可執(zhí)行文件。
②解釋型語(yǔ)言:解釋性語(yǔ)言的程序不需要編譯,相比編譯型語(yǔ)言省了道工序,解釋性語(yǔ)言在運(yùn)行程序的時(shí)候才逐行翻譯。
Python是一種解釋型語(yǔ)言,它的源代碼不需要編譯,可以直接從源代碼運(yùn)行程序。Python解釋器將源代碼轉(zhuǎn)換為字節(jié)碼,然后把編譯好的字節(jié)碼轉(zhuǎn)發(fā)到Python虛擬機(jī)(Python Virtual Machine,PVM)中執(zhí)行。
當(dāng)我們執(zhí)行Python代碼的時(shí)候,在Python解釋器用四個(gè)過(guò)程“拆解”我們的代碼:
首先,當(dāng)你把鍵入代碼交給Python處理的時(shí)候會(huì)先進(jìn)行詞法分析,如果你鍵入關(guān)鍵字或者當(dāng)輸入關(guān)鍵字有誤時(shí),都會(huì)被詞法分析所觸發(fā),不正確的代碼將不會(huì)被執(zhí)行。
Python會(huì)進(jìn)行語(yǔ)法分析,例如當(dāng)"for i in test:"中,test后面的冒號(hào)如果被寫為其他符號(hào),代碼依舊不會(huì)被執(zhí)行。
進(jìn)入最關(guān)鍵的過(guò)程,在執(zhí)行Python前,Python會(huì)生成.pyc文件,這個(gè)文件就是字節(jié)碼。
將編譯好的字節(jié)碼轉(zhuǎn)發(fā)Python虛擬機(jī)中進(jìn)行執(zhí)行:由Python Virtual
Machine(Python虛擬機(jī))來(lái)執(zhí)行這些編譯好的字節(jié)碼。
什么是字節(jié)碼(bytecode)?
簡(jiǎn)單的說(shuō)它就是一個(gè)從源代碼編譯而來(lái)的中間文件(用于不同操作系統(tǒng)平臺(tái)的解釋器執(zhí)行)。比如,a說(shuō)日語(yǔ),b說(shuō)中文,溝通起來(lái)不暢通,請(qǐng)一個(gè)翻譯,把a(bǔ)和b的語(yǔ)言都翻譯成英語(yǔ),這個(gè)英語(yǔ)就可以理解成bytecode, 一種中間語(yǔ)言。
bytecode的好處就是 加載快,而且可以跨平臺(tái), 同樣一份bytecode,只要有操作系統(tǒng)平臺(tái)上有相應(yīng)的Python解釋器,就可以執(zhí)行,而不需要源代碼。不同版本的Python編譯的字節(jié)碼是不兼容的,Python 2.6編譯的bytecode拿到Python 2.7上去執(zhí)行就不行了。
如何生成字節(jié)碼?
Python解釋器一般會(huì)自動(dòng)把.py文件轉(zhuǎn)換成bytecode,然后再執(zhí)行它。當(dāng)你第一次把.py文件當(dāng)作module導(dǎo)入,或者對(duì)應(yīng)的.py文件比.pyc文件的修改時(shí)間還要新時(shí),Python解釋器都會(huì)再?gòu)膕ource code生成相應(yīng)的新bytecode。這樣當(dāng)你下次再次運(yùn)行程序時(shí),就會(huì)直接從bytecode運(yùn)行,從而節(jié)省便宜時(shí)間。
Ps:這里需要注意,有些情況bytecode并不會(huì)生成:
遇到目錄寫權(quán)限的問(wèn)題時(shí)。(比如你編寫代碼和運(yùn)行代碼使用的具有不同權(quán)限的用戶角色,Linux上很常見)
運(yùn)行一個(gè)script并不會(huì)被當(dāng)成是import操作,所以可能也不會(huì)生成bytecode。(比如:你有個(gè)一個(gè)a.py的文件,其中在a.py里,你import了b.py,那么運(yùn)行python
a.py后,會(huì)生成b.pyc,而不會(huì)生成a.pyc)
?拓展閱讀:
(下文詳細(xì)說(shuō)明Python的工作機(jī)制和Python虛擬機(jī)內(nèi)幕)
.pyc文件是什么?
Python源碼編譯的結(jié)果就是 PyCodeObject ,每個(gè)作用域會(huì)編譯出一個(gè)對(duì)應(yīng)的代碼對(duì)象,其中名為co_code的PyStringObject保存著代碼對(duì)象的字節(jié)碼。
一個(gè)Python源文件就是一個(gè)模塊。每個(gè)模塊頂層的代碼對(duì)象通過(guò)marshal序列化之后就得到了.pyc文件。marshal以little-endian字節(jié)序來(lái)序列化數(shù)據(jù)。
那嵌套于頂層作用域里面的那些作用域,例如函數(shù)、類的定義,它們對(duì)應(yīng)的代碼對(duì)象在哪里?它們每一個(gè)都乖乖的躺在上一層作用域的代碼對(duì)象的co_const(常量池)域里,所以其實(shí)頂層代碼對(duì)象已經(jīng)嵌套包含了底下其它作用域的代碼對(duì)象。
?拓展閱讀:
(下文主要結(jié)合實(shí)例說(shuō)明了.pyc文件結(jié)構(gòu))
如何對(duì).pyc文件文件進(jìn)行反編譯?
python文件如果要發(fā)布的話,有時(shí)候還是難免想保護(hù)一下自己的源碼,有些人就直接編譯成了pyc文件,因?yàn)檫@樣既可以保留跨平臺(tái)的特性,又可以不能直接看到代碼,也看到網(wǎng)上很多人說(shuō)為了保護(hù)自己的代碼可以編譯成pyc文件。
用pyc文件可以保護(hù)python代碼的想法其實(shí)是不正確的 ,pyc文件是可以很容易被反編譯的,比如說(shuō)比較著名的uncompyle6 庫(kù)(https://github.com/rocky/python-uncompyle6),用來(lái)反編譯文件最爽不過(guò)了,幾乎支持python全版本的pyc文件的反編譯。
為什么要做代碼分析?
一般來(lái)說(shuō),代碼分析重要性的判斷比較主觀,不同的人有不同的認(rèn)識(shí)。Python是用C來(lái)實(shí)現(xiàn)的,所以對(duì)于Python的性能或代碼質(zhì)量的評(píng)估可以通過(guò) dis模塊 獲取到對(duì)應(yīng)的字節(jié)碼指令來(lái)進(jìn)行評(píng)估。
一般來(lái)說(shuō)一個(gè)Python語(yǔ)句會(huì)對(duì)應(yīng)若干字節(jié)碼指令,Python的字節(jié)碼是一種類似匯編指令的中間語(yǔ)言,但是一個(gè)字節(jié)碼指令并不是對(duì)應(yīng)一個(gè)機(jī)器指令(二進(jìn)制指令),而是對(duì)應(yīng)一段C代碼,而不同的指令的性能不同,所以不能單獨(dú)通過(guò)指令數(shù)量來(lái)判斷代碼的性能,而是要通過(guò) 查看調(diào)用比較頻繁的指令的代碼 來(lái)確認(rèn)一段程序的性能。
一個(gè)Python的程序會(huì)有若干代碼塊組成,例如一個(gè)Python文件會(huì)是一個(gè)代碼塊,一個(gè)類,一個(gè)函數(shù)都是一個(gè)代碼塊,一個(gè)代碼塊會(huì)對(duì)應(yīng)一個(gè)運(yùn)行的上下文環(huán)境以及一系列的字節(jié)碼指令。
dis模塊主要是用來(lái)分析字節(jié)碼的一個(gè)內(nèi)置模塊。dis 模塊的文檔 可以讓你遍歷它的內(nèi)容,并且提供一個(gè)字節(jié)碼指令能夠做什么和有什么樣的參數(shù)的完整清單。
?拓展閱讀:
(下文主要說(shuō)明了dis模塊的使用)
Python開發(fā)者如何寫出高質(zhì)量的代碼?
要不這樣吧,如果編程語(yǔ)言里有個(gè)地方你弄不明白,而正好又有個(gè)人用了這個(gè)功能,那就開槍把他打死。這比學(xué)習(xí)新特性要容易些,然后過(guò)不了多久,那些活下來(lái)的程序員就會(huì)開始用 0.9.6 版的 Python,而且他們只需要使用這個(gè)版本中易于理解的那一小部分就好了(眨眼)。
—— Tim Peters
傳奇的核心開發(fā)者,“Python 之禪”作者
給 comp.lang.python Usenet 小組的留言,2002 年 12 月 23 日,“Acrimony in c.l.p”。
Python 官方教程的開頭是這樣寫的:“Python 是一門既容易上手又強(qiáng)大的編程語(yǔ)言。”這句話本身并無(wú)大礙,但需要注意的是,正因?yàn)樗群脤W(xué)又好用,所以很多 Python 程序員只用到了其強(qiáng)大功能的一小部分。
只需要幾個(gè)小時(shí),經(jīng)驗(yàn)豐富的程序員就能學(xué)會(huì)用 Python 寫出實(shí)用的程序。然而隨著這最初高產(chǎn)的幾個(gè)小時(shí)變成數(shù)周甚至數(shù)月,在那些先入為主的編程語(yǔ)言的影響下,開發(fā)者們會(huì)慢慢地寫出帶著“口音”的 Python 代碼。與此同時(shí),你會(huì)發(fā)現(xiàn),自己在持續(xù)陷入基本的熟練程度,卻無(wú)從提升自己的編程技能。
其實(shí),掌握Python編程不僅要掌握該語(yǔ)言的理論方面, 理解和采用社區(qū)使用的慣例和最佳實(shí)踐也同樣重要。 而且這些技巧可以很好的幫助你避免重復(fù)勞動(dòng),寫出簡(jiǎn)潔、流暢、易讀、易維護(hù)的代碼。
?拓展資料:
PSF研究員、知名PyCon演講者心血之作
Python核心開發(fā)人員擔(dān)綱技術(shù)審校
全面深入,對(duì)Python語(yǔ)言關(guān)鍵特性剖析到位
大量詳盡代碼示例,并附有主題相關(guān)高質(zhì)量參考文獻(xiàn)和視頻鏈接
兼顧Python 3和Python 2
本書致力于幫助Python開發(fā)人員挖掘這門語(yǔ)言及相關(guān)程序庫(kù)的優(yōu)秀特性,寫出簡(jiǎn)潔、流暢、易讀、易維護(hù)的代碼。特別是深入探討了針對(duì)數(shù)據(jù)庫(kù)處理時(shí)生成器的具體應(yīng)用、特性描述符(ORM的關(guān)鍵),以及Python式的對(duì)象:協(xié)議與接口、抽象基類及多重繼承。
上市兩個(gè)月獲 Amazon 百余條五星評(píng)價(jià)
影響全球1 000 000以上程序員的PythonistaCafe社區(qū)創(chuàng)始人Dan Bader手把手帶你提升Python實(shí)踐技能
與《流暢的Python》互為補(bǔ)充,Python進(jìn)階必備
本書致力于幫助Python開發(fā)人員挖掘這門語(yǔ)言及相關(guān)程序庫(kù)的優(yōu)秀特性,避免重復(fù)勞動(dòng),同時(shí)寫出簡(jiǎn)潔、流暢、易讀、易維護(hù)的代碼。用好Python需要了解的最重要的特性、Python 2過(guò)渡到Python 3需要掌握的現(xiàn)代模式、有其他編程語(yǔ)言背景想快速上手Python的程序員需要特別注意的問(wèn)題,等等,本書都可以解決。
參考資料:
https://blog.csdn.net/miaodalengshui/article/details/77451262
https://mp.weixin.qq.com/s/qqHQYyqFsCYVIYjmWOF4jQ
https://linux.cn/article-9816-1.html
https://blog.csdn.net/helloxiaozhe/article/details/78104975
https://www.cnblogs.com/mlgjb/p/7899534.html
總結(jié)
以上是生活随笔為你收集整理的python底层与机器底层关系_由Python历史「解密」Python底层逻辑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数据分析《令人心动的offer2》—你心
- 下一篇: python计算线段夹角