閱讀1004 返回首頁    go Python


Python 元類初探

作者:西北那個峰

先以一個大牛的一段關於Python Metapgramming的著名的話來做開頭:

Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don t (the people who actually need them know with certainty that they need them, and don t need an explanation about why). – Tim Peters

翻譯一下:

Metaclasses是99%的用戶都無需費神的黑科技。如果你還在糾結你是不是需要它的話,答案是NO (真正需要的人根本不需要解釋) - Tim Peters

這是什麼鬼話?道可道,非常道嗎?

好,裝B已畢。這確實是一個冷僻的,不常用的話題。一篇短文肯定講不完。 所以叫做初探。

英文meta這個詞其實是從希臘語裏麵借來的。wikipedia上的解釋是:

indicate a concept which is an abstraction behind another concept, used to complete or add to the latter

不看還好,其實看了更暈。好在後麵的解釋有一句"更高一層的抽象",可以幫助理解。 其實我們可以這樣理解。meta的意思就是"關於什麼的什麼":比如metadata可以理解為"關於數據的數據",metaprogramming可以理解為"關於編程的編程"。這就和"更高一層的抽象" 比較契合了。同時又隱隱和編程中的另一個永恒主題-recursion聯係在了一起。

另外,meta這個詞天朝這邊翻譯成"元",海峽對岸翻譯成"後設"。其實我都不大理解從何而來。

聚焦到我們今天的主題,metaprogramming就是編寫用來生成代碼的代碼。

假設我們寫了一個NB的函數,用來計算一個任意複雜的算數表達式的值:

像1+2, 3*6+10, 什麼的都可以交給它去計算。這樣的函數的算法不是我們的主題,所以我們請出python自帶的大招 ,一行就可以搞定了:

因為輸入的可能性是無限的,所以我們肯定要好好測試一下這個函數了。假定我們想了 上百個test case。又假定我們是用 這個module來做測試的。這樣的測試程序一般會長成這樣:

所以我們的目的就是用metaprogramming的方式來自動產生類似上麵的測試類。

先上程序後解釋:

NB的 函數我們解釋過了。main這段也比較簡單:我們用聲明的方式定義了一組測試,然後通過 來執行。

有點複雜的是 。我們先來看看最內層的 這個方法。邏輯上,它就是把輸入的測試用例分成兩份,一份是 的輸入,一份是我們期待的結果;然後調用 , 接著用 來測試。

但是這個 有點奇怪 - 這裏沒有類,哪裏來的 ? 其實 確實是一個類的方法,隻不過這個類是我們通過代碼動態生成的。也就是下麵這一行:

這裏的 就是通常我們用來檢查某個變量的類型的那個函數。隻不過它還有另外一種不大為人知的形式:

這第二種形式,就會產生一個新的類型。以我們的程序為例,就是以 為baseclass, 產生了一個名為 的新類型,改類型的實現由 給出,而 就包含了通過closure返回的 這個方法。隻不過在這個新類裏麵,它的名字叫做 。

最後,我們把這個新產生的類加入到當前全局符號表裏麵,也就相當於上麵給出的unittest的例子。

所以,總結一下。當我們運行這個腳本的時候,這段比較短的代碼會針對每一個測試的表達式產生一個新的測試類,並動態生成測試的方法加載到該類裏麵。 從 中找到這些類並一一執行測試。

上麵的例子中,其實一行一行手打 也沒什麼大不了的。但是當你要表達的邏輯比較複雜的時候,metaprogramming的強大就體現出來了。

那麼,看完這篇文章,我們也成為Tim所說的1%的程序猿了!其實,也許他的意思是,99%的編程工作都用不到這樣技巧。在一些特殊的場合,比如編寫某種框架的時候,metaprogramming會做到事半功倍。祝你在實踐中碰到這樣的機會。

題圖:pexels,CC0 授權。

最後更新:2017-10-25 11:57:49

  上一篇:go 解密:Python風靡全宇宙,首要原因竟是它?
  下一篇:go Python GUI入門學習案例!這是最好玩的Python係列!附帶源碼!