當前位置:
首頁 > 知識 > 為什麼你應該學 Python ?

為什麼你應該學 Python ?

(點擊

上方藍字

,快速關注我們)




編譯:伯樂在線 - 逆旅 


如有好文章投稿,請點擊 → 這裡了解詳情




引言




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



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




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




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




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





#include <stdio>


 


int

main

(

void

)

{


    

FILE

*

fp

;

    

char

buff

[

256

];

// assuming a line won"t contain more than 256 chars


    

fp

=

fopen

(

"hello.txt"

,

"r"

);


 


    

while

(

fgets

(

buff

,

256

,

fp

))

{


        

printf

(

"%s"

,

buff

);


    

}


 


    

fclose

(

fp

);


    

return

0

;


}




python 這麼做:





with

open

(

"hello.txt"

)

as

f

:


    

for

line

in

f

:


        

print

(

line

)




現在,很多人會認為這是 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下,你要這麼做:





for f in *; do echo "Processing $f file..."; done




用 PowerShell 做類似的事情:





Get

-

ChildItem

"."

|


Foreach

-

Object

{


    

$

name

=

$

_

.

Name


    

Write

-

Output

"Processing $($name) file..."


}




An equivalent functionality in Python can be achieved with:




python 這麼做:





from os import listdir


 


for

f

in

listdir

(

"."

)

:


    

print

(

"Processing {} file..."

.

format

(

f

))




現在我認為,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 ,內容如下:





cat


dog


mouse


bird


cat


cat


dog




現在使用我們的腳本:





$> cat names.txt | namecount.py




Powershell:





$> Get-Content names.txt | python namecount.py




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





bird

1


mouse

1


cat

3


dog

2




namecount.py 源碼:





#!/usr/bin/env python3


import sys


 


def count_names

()

:


    

names

=

{}


    

for

name

in

sys

.

stdin

.

readlines

()

:


        

name

=

name

.

strip

()


 


        

if

name

in

names

:


            

names

[

name

]

+=

1


        

else

:


            

names

[

name

]

=

1


            


    

for

name

,

count

in

names

.

items

()

:


        

sys

.

stdout

.

write

(

"{0} {1}
"

.

format

(

name

,

count

))


 


if

__name__

==

"__main__"

:


    

count_names

()




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




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





cat

3


dog

2


bird

1


mouse

1




(旁註:如果你用 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 後,發現有 「魔法」。和這種情況類似:





from

bs4

import

BeautifulSoup


 


soup

=

BeautifulSoup

(

"<p class="someclass">Hello</p>"

,

"html.parser"

)


soup

.

p




上面的代碼利用第一個字元串參數創建了一個 BeautifulsSoup 實例,第二個參數表示我想使用 Python 自帶的 HTML 解析器(BeautifulSoup 可以搭配多種解析器)。soup.p 返回一個 Tag(bs4.element.Tag) 對象,表示將作為第一個參數。




以上代碼的輸出是:





<p class="someclass">Hello</p>




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





from

bs4

import

BeautifulSoup



soup

=

BeautifulSoup

(

"<foobarfoo class="someclass">Hello</foobarfoo>"

,

"html.parser"

)


soup

.

foobarfoo




The output is the following:




輸出如下:





<foobarfoo class="someclass">Hello</foobarfoo>




當我發現這樣也能運行,我的反應是「怎麼回事?」。因為,第一個例子很容易實現,我的意思是最直接的方法是為每一個 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 相同屬性的內容的能力。下面是代碼:





class

Person

(

object

)

:


    

def __init__

(

self

,

first_name

)

:


        

self

.

first_name

=

first_name


 


    def __getattr__

(

self

,

name

)

:


        

if

(

name

==

"name"

)

:


            

return

self

.

first_name


        raise AttributeError

(

"Person object has no attribute "{}""

.

format

(

name

))




我們在終端運行代碼:





person

=

Person

(

"Jason"

)


>>>

person

.

first

_

name


"Jason"


 


>>>

person

.

name


"Jason"


 


>>>

person

.

abc


Traceback

(

most recent call

last

)

:


  

File

"<stdin>"

,

line

1

,

in

<

module

>


  

File

"<stdin>"

,

line

7

,

in

__getattr__


AttributeError

:

Person

object

has no

attribute

"abc"




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





class

Dog

(

object

)

:


    

def bark

(

self

)

:


        

print

(

"Ruff, ruff!"

)


    


    

def __getattr__

(

self

,

name

)

:


        

if

(

name

==

"meow"

)

:


          

return

lambda

:  

print

(

"Meeeeeeow"

)


        

raise AttributeError

(

"I don"t know what you"re talking about..."

)





>>>

snoop

=

Dog

()


 


>>>

snoop

.

bark

()


Ruff

,

ruff

!


 


>>>

snoop

.

meow

()


Meeeeeeow




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




意思是:





class

Dog

(

object

)

:


    


    

def __init__

(

self

)

:


        

self

.

name

=

"Doggy Dogg"




等價於:





class

Dog

(

object

)

:


    


    

def __init__

(

self

)

:


        

self

.

__dict__

[

"name"

]

=

"Doggy Dogg"




兩者輸出是一樣的:





snoop

=

Dog

()


 


>>>

snoop

.

name


"Doggy Dogg"




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




如果你還想了解更多,可以查看文檔中的 Description Protocol。




Python 的面向對象




Python 的面向對象有點奇怪。例如,類中沒有私有變數和方法。所以你想在類中創建一個實例變數或私有方法,你必須遵守規則:






  • 一個下劃線 (_)表示私有變數和方法。



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




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





class

Foo

(

object

)

:


    

def __init__

(

self

)

:


        

self

.

public

=

"public"


        

self

.

_private

=

"public"


        

self

.

__secret

=

"secret"




轉到解釋器:





>>>

foo

=

Foo

()


>>>

foo

.

public


"public"


>>>

foo

.

_private


"public"


>>>

foo

.

__secret


Traceback

(

most recent call

last

)

:


  

File

"<stdin>"

,

line

1

,

in

<

module

>


AttributeError

:

"Foo"

object

has no

attribute

"__secret"




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





>>>

foo

.

_Foo_

_

secret


"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》 ——比上面的主題要多,你會學到如何寫一個常見的用於實戰的滲透測試,取證分析和安全腳本。




看完本文有收穫?請轉

發分享給更多人


關注「P

ython開發者」,提升Python技能


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

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


請您繼續閱讀更多來自 Python開發者 的精彩文章:

Gevent 調度流程解析
入門 Python 要多久?
遺傳演算法中適值函數的標定與大變異演算法
用 Scikit-Learn 和 Pandas 學習線性回歸
Python 源碼閱讀:類型

TAG:Python開發者 |