為什麼你應該學 Python ?

逆旅發表於2017-09-20

引言

第一次接觸 Python 是在一節程式設計入門課上。其實,在此之前瞭解過它,所以在上課之前我對它的語法已經很熟悉了,但在上課之前我沒有用它做過真正的專案。儘管對它沒有太大興趣,但我認為把它介紹給人們去學習程式設計還是很好的。我對它不是不喜歡,而是一種“無所謂”的態度。原因很簡單:它裡面有太多“魔法”。 C 和 Java 這些語言,對底層的行為描述的很清晰,Python 則完全相反。

另外,Python 結構鬆散:寫大型複雜程式時,遇到規則嚴謹的程式結構體(比如每個檔案一個公共類),比其他語言(比如 Java )要費些力氣。但是,在這些方面 Python 給了你很大的自由。

另一件事是嚴格的編碼風格和除錯:因為Python 是解釋型語言,查詢問題不太容易:如果C 語言有語法錯誤,編譯器會直接停止編譯,但在解釋型語言中,直到執行到問題行,問題才會被發現。試著在需要整數的時候傳一個字串?cc 會馬上提醒你,Python 直譯器卻對此一點都不介意(雖然有工具可以發現這個問題,比如 mypy,但我討論的是通用的Python)。我提到的這些問題是解釋型語言的通病,並非 Python 獨有,但這些是我不喜歡它的主要原因。

還有一個煩人的問題是強制縮排。我們老師(很優秀)認為這是好事情,因為“它強制我們形成簡潔的程式碼風格”。確實如此,但還是有點煩,當程式碼沒有按預期執行時,你分析程式碼想要找出 bug,它卻無影無蹤,過了很長時間之後你發現 if 語句那一行有一個多餘的空格。

我曾經和同事聊過 Python,告訴他為什麼我之前對這個語言不感冒,他笑著問我“問什麼不喜歡Python呢?因為它讀起來很像英語?”。是的。因為這個語言做了很多底層的工作,有時候會不清楚發生了什麼。舉個讀檔案的例子,假設你想一行一行讀取檔案內容並列印出來。C 會這麼做:

python 這麼做:

現在,很多人會認為這是 python 的優勢,然而,第一個例子中,幹了什麼一目瞭然:

  • 獲取一個檔案指標
  • 從檔案讀取每一行資料到快取中,列印快取中的內容
  • 關閉檔案流

python 的例子中看不到這些,它是一種 “魔法般的”過程。現在,有人認為這是好事,因為將程式設計師與底層實現細節隔離(我同意這個說法),但我想知道到底發生了什麼。

有趣的是,我以上提到的缺點,我現在認為都是優點。為了公平起見,我強調,Python 裡邊沒有魔法,如果你多瞭解一點,你會發現真的沒有,有的只是語言解釋程式碼的方式,從這點來看,我發現它挺有意思的。如果你也這麼覺得,我建議你深入瞭解它的工作機制,如果有東西像魔法,就找出來到底發生了什麼,事情就會變得清晰,魔法就變成了便利。

我的認識發生很大的變化,尤其是我決定使用 Python 後,事實上我現在是 Python 的死忠!現在你也許會想我將會在哪裡說服你學 Python 是個好主意,不要擔心,馬上就到。作為引言的結尾,我想說明,這只是我對這個語言的個人感受,只是個人偏好。我沒有試圖以“如果你用 Python,你就不是真正的程式設計師(實際上,我不這麼認為)”的理由勸說人們學 C。當有人問我他們的入門語言應該選哪個,我通常建議他們選 Python,基於我上邊提到的“缺點”的原因。我的感覺來源於我的興趣,我曾經在做一些很底層的東西,你能想到,Python 並不適用。

Python 語言精粹

在借用了JavaScript 暢銷書 《JavaScript 語言精粹》作為本節標題後,我們開始討論本文的主題:為什麼你(沒錯,就是你!)應該學 Python。

1、通用指令碼語言

這是我使用 Python 的主要原因。我曾經和很多人做過很多專案,不同的人用不同的系統。就我而言,我經常在windows系統和linux系統之間切換。舉一個實際的例子,有一個專案,我寫了專案的自動測試指令碼,結果發現只有我能用,因為是用 PowerShell 寫的,而我是專案中唯一使用 Windows 的。當時同事們自然認為 bash 是最好的,我還向他們解釋 PowerShell 遵循一種不同的模式並且有它的強項(例如,它提供了 .NET 框架介面),它是物件導向的指令碼語言,和 bash 完全不一樣。現在我不想討論哪個更好,因為這不是本文的重點。

那麼這個問題怎麼解決呢?嗯…現在,是否有一種指令碼語言可以在所有主流平臺上執行呢?你猜對了,它就是 Python。除了可以在主流平臺上執行,它還是開箱即用的指令碼語言。標準庫包含不少實用程式,提供了獨立於系統的常用介面。舉一個簡潔明瞭的例子,假設你想獲取資料夾下所有檔案的檔名,然後對其進行處理,在 UNIX下,你要這麼做:

用 PowerShell 做類似的事情:

An equivalent functionality in Python can be achieved with:

python 這麼做:

現在我認為,Python 除了可以跑在 Linux,MacOSX 和 Windows 上,它也很易讀。上邊例子中的指令碼很簡單,在複雜的例子中不同語言的易讀性差異會更明顯。

就像我之前提到的,Python 自帶了許多強大的庫用來取代 shell 指令碼,你會發現,最有用的是:

      • os – 提供系統無關功能,比如檔案目錄和檔案讀寫。
      • subprocess – 產生新程式、與輸入輸出流和返回程式碼互動。可以用它來啟動系統已安裝的程式,但請記住如果你擔心指令碼的可移植性,這不是最好的選擇。
      • shutil – 提供對檔案和檔案集合的高階操作。
      • argparse – 解析命令列引數,構建命令列介面。

好了,假設你 get 到了重點,跨平臺和易讀性聽起來挺不錯的,但是你真的喜歡類 UNIX shell 類似的語法怎麼辦?告訴你個好訊息,魚和熊掌可以兼得!看看 Plumbum,它是一個 Python 模組,它的座右銘是“ 再也不寫 shell 指令碼”。它模仿了 shell 語法,同時保持了跨平臺。

不要完全拋棄 shell 指令碼

即使 Python 可以完全取代 shell 指令碼,但也不是必須這麼做,因為 Python 指令碼天生適合 Unix 命令列理念,你要做的就是讓它們從 sys.stdin (標準輸入)讀資料,向 sys.stdout(標準輸出)寫資料。舉個例子,假設你有一個檔案,每行有一個單詞,你想知道每個單詞在文中出現的次數。這種情況就沒必要全部是用Python,我們可以使用 cat 命令和我們的指令碼,稱它為 namecount.py 一起來完成這個任務。

假設有一個檔案,名為 names.txt ,內容如下:

現在使用我們的指令碼:

Powershell:

期望的輸出如下(順序可能會變化):

namecount.py 原始碼:

無序的資訊可讀性差,你可能想按單詞出現的次數對其排序,讓我們試試。我們要用管道輸出檔案內容供內建命令處理。按數字降序排序,我們要做的就是 $> cat names.txt | namecount.py | sort -rn 。如果使用PowerShell 應該這樣:$> Get-Content names.txt | python namecount.py | Sort-Object { [int]$_.split()[-1] } -Descending (你可能聽到了 Unixer 的吐槽聲了,PowerShell 怎麼這麼繁瑣)。

這回我們的輸出是確定的,如下所示:

(旁註:如果你用 PowerShell,cat 是 Get-Content 的別名,sort 是 Sort_object 的別名,所以以上命令可以寫成:$> cat names.txt | python namecount.py 和 $> cat names.txt | python namecount.py | sort { [int]$_.split()[-1] } -Descending )

但願我成功說服你 python 是你某些指令碼的替代品,你不必完全拋棄 shell 指令碼,因為你可以將 Python 融合到你現有的工作流和工具箱中,還可以從它跨平臺,更好的可讀性,還有豐富的庫中獲益(後面會講)。

2、大量優秀的庫

Python 有非常豐富的庫。我的意思是,幾乎任何事都有庫(有趣的是:如果你在你的Python 直譯器中輸入 import antigravity,在瀏覽器中開啟 xkdc 漫畫的頁面,是不是很酷?)。我不是很推崇堆疊模組式的程式設計,但你不必這樣。因為有太多的庫,不表示你都要使用。我也不喜歡堆疊模組(它有點像 CBSE),我在瞭解它們之後才使用。

例如,我決定研究馬爾科夫鏈,我想了一個專案:抓取一個藝術家的所有歌詞,建立一個馬爾科夫鏈,然後從其中生成歌曲。這個專案的目的是生成的歌曲應該能反映出藝術家的風格。所以我到處找相關的東西,搞出了 lyricst 專案(這只是個樣品,還不成熟,只是一個測試專案,如我所言,我只是隨便搞了一下,沒想深入。如果你想玩的話,它包含有命令列介面和示例的說明文件)。我認為,最好的找歌詞的地方是 RAPGenius,因為它很活躍,經常更新。

為了獲取藝術家所有的歌詞,我必須從網站上爬,然後處理 HTML。幸運的是,Python 很適合做網路爬蟲,它有強大的庫像 BeautifulSoup 可以處理 HTML。所以我是這麼做的,先使用 BeautifulSoup 從網頁中抽取我需要的資訊(就是歌詞)然後用這些資訊構建馬爾科夫鏈。當然我曾經想用正規表示式構建自己的 HTML 解析器,但是這個庫的存在讓我更關注專案的最終目的:把玩馬爾科夫鏈,讓它更有趣,比方說,從檔案中讀取些內容出來。

3、用來做滲透測試很強大

如果你在作滲透測試或僅僅是喜歡玩玩,Python 是你的好幫手!由於Python 在所有 LInux 和 MAC OS 機器上都有安裝,還有豐富的庫,完善的語法,還是一門指令碼語言,讓它很適合幹這個。

另一個我為什麼決定使用 Python 的原因(除了我之前提到的)是我對安全很感興趣,Python 是用來做滲透測試的完美選擇。我在第一次進入領域是通過 Scapy(或 Scapy3k ,python3),我印象很深。Scapy 能夠建立、監聽、解析資料包。它的 API 很簡單,文件也很完善。你可以很容易的建立不同層的資料(我指的是 OSI 模型)或者捕獲它們對其進行分析或修改。你甚至可以匯出 pcap 檔案用 Wireshark 開啟。雖然除了抓包還能做很多事情,還有很多其他的庫也可以,但我在這裡不會涉及,因為這不是本文的重點而且要展開講的話需要一篇文章。

有人可能會說,“哦,太棒了,但我感興趣的是 Windows 裝置,裡邊不會自帶 Python”。別當心,你可以用 py2exe 把你的指令碼編譯成 .exe 檔案。檔案可能會有點大(取決於你是用的庫的數量),但這不是重點。

如果你很好奇,請參考 list of Python pentesting tools。 文末我還推薦了幾本書。

4、黑客的語言

Python 是可塑性很強的語言。你可以用各種方法改造它。可參見《 altering the way imports work》和《messing with classes before they are created》這兩篇文章。這只是一些例子。也讓它成為強大的指令碼語言(在第一節有說)適合做滲透測試(第三節),因為它給了你很大的自由。

我不想講太多,但我會講述它讓我驚訝的地方。當時,我在做一個網路爬蟲( Python 很適合幹這個!),我用的其中一個工具是 BeautifulSoup。 這是我用來學習 Python 的專案之一。Beautifulsoup 處理 HTML 的語法清晰直觀,原因是在自定義行為方面,Python 給了你很大的自由。瞭解一番 API 後,發現有 “魔法”。和這種情況類似:

上面的程式碼利用第一個字串引數建立了一個 BeautifulsSoup 例項,第二個參數列示我想使用 Python 自帶的 HTML 解析器(BeautifulSoup 可以搭配多種解析器)。soup.p 返回一個 Tag(bs4.element.Tag) 物件,表示將作為第一個引數。

以上程式碼的輸出是:

現在你可能會想,你說的魔法在哪?馬上就來。魔法在於上面的程式碼可以被修改為任何標籤,甚至可以是自定義的。它意味著下面的程式碼也可以正常執行:

The output is the following:

輸出如下:

當我發現這樣也能執行,我的反應是“怎麼回事?”。因為,第一個例子很容易實現,我的意思是最直接的方法是為每一個 HTML 標籤定義一個屬性(例項變數),在解析過程中如果找到了,就賦值給它們。但是這對第二種情況不適用,不可能對所有的字串定義屬性。我想知道它是怎麼實現的,所以我開啟 BeautifulSoups 原始碼開始尋找。 我沒有發現任何命名為 p 的屬性,這一點也不奇怪,解析函式沒有對其賦值。谷歌一番後,我找到了答案:魔法方法。什麼是魔法方法,為什麼要叫這個名字?事實上,魔法方法是給你的類賦予魔法的方法。這種方法通常前後有兩條下劃線(例如 __init__()),在Python文件的 DataModel model section 有對它的說明。

真正讓 BeautifulSoup 擁有這個功能的魔法方法是__getattr__(self, name)(self 在python 中指向例項,和 Java 中的this 類似)。如果去檢視文件,你會發現第一段如下:

如果在屬性常見地方找不到屬性時,比如既不是例項屬性,又沒有在 self 類樹中找到,則呼叫該方法(object.__getattr__(self, name))。引數 name 就是屬性名這個方法應當返回(計算過的)屬性值或丟擲 AttributeError 異常。

當你嘗試訪問一個不存在的屬性,物件的 __getattr__(self,name) 方法會被呼叫,將返回一個以name 作為名字的屬性的字串。

舉個例子。假設你有一個 Person 類,擁有 first_name 屬性。我們給使用者訪問和 name 相同屬性的內容的能力。下面是程式碼:

我們在終端執行程式碼:

這意味著我們能憑空構造例項屬性,是不是很棒?所以你可以偷偷的讓你的 Dog 除了汪汪叫之外,還會喵喵叫:

你可以在沒有 reflection 的情況下,隨意新增新屬性。object.__dict__ 是(字典)[https://docs.python.org/3.5/library/stdtypes.html#typesmapping] 包含 object 的屬性和它們的值(注意我說的是 object.dict, object 是一個例項,還有一個 class.dict,是類的屬性的字典)。

意思是:

等價於:

兩者輸出是一樣的:

到這裡你會想,是挺好的,但是有什麼用呢?答案很簡單:magical APIs。你有沒有用過一些 Python 庫讓你感覺像魔法?這是讓它們變的有”魔法”的一種情況。雖然一旦你懂了底層發生的事情,就會發現沒有魔法。

如果你還想了解更多,可以檢視文件中的 Description Protocol

Python 的物件導向

Python 的物件導向有點奇怪。例如,類中沒有私有變數和方法。所以你想在類中建立一個例項變數或私有方法,你必須遵守規則:

      • 一個下劃線 (_)表示私有變數和方法。
      • 兩個下劃線(__) 表示的變數和方法,它們的名字會被修改。

舉個例子,假設你有如下類:

轉到直譯器:

如你所見,你可以訪問 _private 變數,但是最後一個例子發生了什麼,它是否意味著有兩個下劃線的變數是真正的私有變數?答案是 NO,它們的名字被改變了,實際上,它被 Python替換成了 _Foo_secret 。如果你想訪問的話,你仍然可以訪問:

然而,PEP8 建議只在父類中使用雙下劃線來避免屬性名衝突。“PEP”,表示 “Python Enhancement Proposal”,它用來描述 Python 特性或作用。如果你想要新增一個新特性,你可以建立一個 PEP,這樣可以讓整個社群可以看到並討論。你可以在這裡瞭解更多的 PEPs

可見,Python 很信任程式設計師。

我不會再深入講 OO 了,因為它需要單獨一篇文章(甚至是一系列)來講解。

我確實想給你提個醒,Python 的 OO 可不像 Java 語言那麼自然,你需要慢慢適應,但你知道嗎,它只是做事的方法不同而已。舉個例子,它沒有抽象類,你必須使用裝飾器來實現這個行為。

結語

希望這篇文章,能夠給你一個學習 Python 的理由。這篇文章來自一個為過去說了Python 的壞話而愧疚,如今在到處宣傳 Python 的人。我先申明一點,這只是個人喜好問題,當有人問我先學哪門語言時,我通常推薦 Python。

如果你還沒決定,那就給它一次機會!用上一兩個小時,多讀些關於它的東西。如果你喜歡從書上學習,我也會幫你,看看《Fluent Python》, 下節還有更多。

書籍推薦

我兌現了諾言,這一節推薦書籍。我會盡量保持簡短一些,只包含一些我讀過的書籍。

  • 《Fluent Python》 —— 一本講 Python3 的好書。無論你是新手、熟手還是高手都值得一讀。包含了 Python 的來龍去脈。
  • 《Web Scraping With Python》 —— 標題已經說明了一切,講如何用Python 來做網路爬蟲。你會探索如何爬網上的內容,解析 HTML 等。我覺得這本書對爬蟲領域的新手和熟手很有幫助。即使你之前從沒用過Python,你也可以看懂。它沒有涉及任何高階主題。
  • Black Hat Python》 —— 這個有趣!你可以建立反彈 SSH shell,木馬等等!如果你想知道 Python 如何做滲透測試,請一定要讀它。注意它使用的是 Python 2,我有一個倉庫,用的是 Python 3。
  • Violent Python: A Cookbook for Hackers, Forensic Analysts, Penetration Testers and Security Engineers》 ——比上面的主題要多,你會學到如何寫一個常見的用於實戰的滲透測試,取證分析和安全指令碼。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

為什麼你應該學 Python ?

相關文章