1004
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