Requests 網(wǎng)絡(luò)庫學(xué)習(xí)之「從低版本開始」

一、扯淡

從上一篇 Requests的用法 到現(xiàn)在已經(jīng)過去半個多月了,這半個多月我一直在抽時間看 Requests 源碼,所以今天將這半個月學(xué)習(xí)到的零零碎碎知識分享出來,以供大家學(xué)習(xí)和批評。

  • 優(yōu)秀的開源代碼

優(yōu)秀的開源代碼就是有諸多的開發(fā)者在持續(xù)維護(hù)更新且開發(fā)者極其活躍。不像某些開源代碼熱于一時,后續(xù)再沒見過有提交記錄,也許就像大眾那么講的:

“他們就是為了拿到很高的Star,然后沖沖 KPI”

  • Requests屬不屬于優(yōu)秀的開源代碼?

我們來先看看它的提交記錄匯總,如下圖:



(Requests 提交匯總)

基本上每月都會有 N 次提交,當(dāng)然目前貢獻(xiàn)者是來自全球各個地方的 Pythoneer,而最終由 kennethreitz 本人來做合并以保證代碼的質(zhì)量。


(Requests 最近提交記錄)

這是 Requests 最近的提交記錄,你會發(fā)現(xiàn) kennethreitz 剛做過一次合并且目前最新的版本是 v2.18.1,其次可以看到 commit 的力度特別細(xì)、非常詳細(xì)。

  • 學(xué)習(xí)一份優(yōu)秀的開源代碼,如何開始?

我相信很多人都是直接拉到最新代碼開始看,然后哼哧哼哧的啃上好幾個月,最后也就有個大概的理解。

但,對于學(xué)習(xí)開源代碼、夢想著自己也能寫出來這么優(yōu)秀代碼的開發(fā)者來說,我不建議直接上手最新的代碼。直接上手最新的代碼,到后面你也只能了解到代碼的調(diào)用邏輯。

  • 舉個例子:造飛機(jī)

如果你想知道飛機(jī)是怎么造起來、怎么飛起來的話,你可能只需要知道飛機(jī)上有哪些零件,這些零件的作用都是什么。但是如果讓你造一架飛機(jī)出來,就拿你知道的那些毛皮知識根本沒法造起來,這時候你就需要深入到這些零件的制造工藝和那些飛行動力學(xué)的原理。所以,你的第一步就需要去了解 萊特兄弟 當(dāng)時造出來的第一架飛機(jī),畢竟當(dāng)時就一個空架子里面加了幾個引擎和架子的設(shè)計(jì)、算法。掌握了從 0 開始的基礎(chǔ),然后你就可以嘗試大步跳躍的了解之后的5代、10代、20代 乃至當(dāng)今最牛逼的造飛機(jī)工藝。

  • 開始著手學(xué)習(xí)

學(xué)習(xí)優(yōu)秀的開源代碼如同造飛機(jī)一樣,我們需要從開源代碼的最早版本開始,然后一步步去學(xué)習(xí)。

git tag  # 可以羅列出當(dāng)前代碼的所有版本號
git checkout [tag_version] # 可以直接切換到對應(yīng)版本的代碼

當(dāng)然,你也可以直接在 Github 上查看 tags 。



(Requests 版本列表)

這是 Requests 的版本列表,目前版本已經(jīng)更新到了 v2.18.1 ,也就是說從 11 年 Requests 開源出來已經(jīng)經(jīng)歷了無數(shù)個版本的迭代,才更新到目前相對比較成熟的版本。

那么我們先來切換到 v2.18.1 代碼。

git checkout v2.18.1

(Requests v2.18.1源代碼)

這是 Requests 的 v2.18.1 版本,也就是最新版本的源代碼。這個版本中雖然只有這么十幾個文件,但是大部分都是1k行的代碼,對于新手來講看起來非常費(fèi)勁。

因此我這里強(qiáng)烈建議:如果你不是經(jīng)常看源代碼的老司機(jī),那么就從一個開源項(xiàng)目的最早版本開始看。因?yàn)椋婚_始的版本都是非常簡單且易懂,就拿 Requests 舉例:

git checkout v0.2.0

我們將代碼切換到 Requests 最早的版本。


(Requests v0.2.0 版本代碼)

可以看到這是 kennethreitz 本人親自提交的。其中只有 init.py 和 core.py 兩個源代碼,而且 core.py 中只是針對 urllib 做了一層封裝,并將接口暴露之后。雖然第一個版本簡單,但是有很多代碼細(xì)節(jié)我們至少能一眼看到,并且學(xué)習(xí)到。

同樣的方法,我們再直接跳到 V0.3.0的代碼。



(Requests v0.3.0 版本代碼)

你會發(fā)現(xiàn) v0.2.0 到 v0.3.0 的改動并不多,只是增加了一個 packages 的目錄,這個目錄是直接引入第三方 poster 的庫。其次就是增加了 async.py,這個文件是用來處理 Requests 并發(fā)問題,用到了 urllib, urllib2, eventlet 和 gevent 做兼容。最后就是代碼風(fēng)格的改動,更加符合 Pythonic 的語法。

那么接下來,我就分享下最近從 Requests 低版本中學(xué)到的知識。

二、注釋

從 Requests 第一個版本開始,你就會發(fā)現(xiàn)有很詳細(xì)的接口注釋。



(Requests 中 Get 函數(shù)的代碼片段)

Python 中的注釋有多種形式,有單行注釋、多行注釋和批量注釋。Python 注釋有著自己的規(guī)范。

注釋的意義就不多講了,簡單的來說就是提供給別人看懂你代碼的幫助。

  • 單行注釋

井號(#)被用作單行注釋符號,在代碼中使用 # 時,它右邊的任何數(shù)據(jù)都會被忽略。

  • 批量、多行注釋符號

在 Python 中也會有注釋很多行的情況。這個時候就需要用到三引號 ''' ''' 包含。例如 Requests 的注釋。

那么,對外提供的接口該如何注釋?

在三引號 """ ... """ 中使用 冒號(:)后面緊跟 param + 參數(shù)名 : 參數(shù)的含義。這樣就能很清晰的讓別人理解你的代碼。



(get 函數(shù)代碼注釋)

三、setattrgetattrcall

在 Requests 中最重要的兩個類就是 Request 和 Response,Request 類決定了請求的參數(shù)并提供了請求的接口,而 Response 提供了請求結(jié)果的封裝。


(Request 類的代碼片段)

Request 這個類提供了幾個參數(shù):


其中有個方法 setattr ,是用來判斷傳入的請求協(xié)議是否屬于 _METHODS 定義的那幾種。若不在,則拋出無效方法的錯誤。

  • setattr

用來設(shè)置對象的屬性,通過 object 中的 setattr 屬性來設(shè)置。
就像 requests 中,setattr 用來過濾不支持的請求方式。

  • getattr

內(nèi)建函數(shù),可以通過函數(shù)名來獲取。



因此,可以通過 'getattr' 來實(shí)現(xiàn)工廠模式


  • call

用于實(shí)例自身的調(diào)用。也就是說如果定義了 call 方法,那么你的實(shí)例可以直接調(diào)用 () ,也就類似于 .call()。

四、Mixin模式

在我們寫代碼的過程中,不乏會出現(xiàn)很多需要代碼復(fù)用的地方。也就類似于人類科技的進(jìn)步,當(dāng)你發(fā)現(xiàn)在同一件事上需要做很多遍的時候,那么這件事情必然可以被自動化,將操作過程復(fù)用以提高生產(chǎn)力。

所以,Mixin 是在 Python 乃至其他語言中定義的一種有利于代碼復(fù)用的模式。

Mixin 本身其實(shí)是一種能力的承諾,它有部分或者全部的實(shí)現(xiàn)可被復(fù)用。

當(dāng)然,在不同的語言或者框架中,對 Mixin 模式有不同的實(shí)現(xiàn)形式, Python 中除了 protocol,也可以用多繼承的形式來實(shí)現(xiàn)Mixin,為了區(qū)別普通的多繼承,同時 Mixin 類的類名一般都會帶上后綴: " Mixin "。

比如在 Python Lib 里面的兩個 Mixin 類:

UserDict.DictMixin  
SocketServer.ForkingMixin。

我們再回到 Requests 源碼,其中同樣出現(xiàn)了 Mixin 的引入。在 v0.4.0 版本中針對 Response 的 Headers 就用到了 DictMixin。



(Requests 請求結(jié)果的代碼片段)

其中 CaseInsensitiveDict 就繼承自 DictMixin 。



(CaseInsensitiveDict 代碼片段)

這樣做的好處就是 Requests 可以直接將請求結(jié)果中的 Headers 數(shù)據(jù)轉(zhuǎn)換為 Dict,同時也可以擴(kuò)展 Headers 的函數(shù)。

所以,Mixin 其實(shí)是一種思想,用部分實(shí)現(xiàn)的接口來實(shí)現(xiàn)代碼復(fù)用。可以解決多繼承的問題,又可以用來擴(kuò)展功能。

五、@staticmethod 和 @classmethod

  • 簡介

這兩個關(guān)鍵字是 Python 內(nèi)建的裝飾器。一般來說,我們要使用某個類的方法的時候,需要先實(shí)例化一個對象再調(diào)用對應(yīng)方法。

而如果函數(shù)中使用了 @staticmethod 和 @classmethod 來修飾,就可以不需要實(shí)例化之后再調(diào)用,直接類名.方法名() 來調(diào)用。這樣有利于組織代碼,把某些應(yīng)該放在屬于某個類的函數(shù)給放到那個類里面去,同時有利于命名空間的整潔。

  • 區(qū)別

既然這兩種裝飾器 @staticmethod 和 @classmethod 都可以直接類名.方法名()來調(diào)用,那么它們的區(qū)別是什么呢?

從它們的使用上來看:

@staticmethod 不需要表示自身對象的 self 和自身類的 cls 參數(shù),就和普通函數(shù)一樣。
@classmethod 也不需要 self 參數(shù),但第一個參數(shù)是表示自身類的 cls參數(shù)。
  • 使用

有這么一種情況,我們的函數(shù)中有時候會用到類中的屬性和方法。

針對這種情況,如果該函數(shù)是 @staticmethod 來修飾的,那么只能通過類名.屬性名或者類名.方法名來調(diào)用。同樣的,如果該函數(shù)是 @classmethod 修飾,由于該函數(shù)持有了 cls 參數(shù),因此可以直接用來調(diào)用類的屬性、類的方法、實(shí)例化對象等,避免了硬編碼。

參考代碼:



(@staticmethod 和 @classmethod 例子)

  • Requests 中的用例

同時,我們來看看 Requests 中應(yīng)用到的代碼片段。



(Requests 中用到的 @staticmethod)
這是封裝在 Request 類中的一個靜態(tài)方法,用來生成開發(fā)者指定的請求URL 和 請求參數(shù) data 格式化后的請求地址。

六、總結(jié)

看完了上面針對 Requests 低版本的學(xué)習(xí)分享,這時你也可以著手 Requests 低版本開始學(xué)習(xí),我相信你會掌握更多 Python 中基礎(chǔ)和高階的知識點(diǎn)。

雖然,這些知識點(diǎn)看起來都很簡單,但在實(shí)際開發(fā)中確實(shí)能幫助到我們很多。同時,又提高我們的代碼水平,讓我們代碼更加優(yōu)雅、更加風(fēng)騷、更加 Pythonic。

后面的時間,我將會繼續(xù)深入學(xué)習(xí) Requests 源碼,技術(shù)點(diǎn)也會更多,畢竟 Requests 代碼如此的風(fēng)騷。同時,我也將持續(xù)分享學(xué)習(xí)總結(jié)。

長摁‘識別二維碼’,一起進(jìn)步

生活不止眼前的茍且,還有手下的代碼、
和嘴上的扯淡
——
個人博客: http://xiyoumc.0x2048.com
Github:https://www.github.com/xiyouMc

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容