摘要:以我們的程序?yàn)槔?,就是以為產(chǎn)生了一個(gè)名為的新類(lèi)型,改類(lèi)型的實(shí)現(xiàn)由給出,而就包含了通過(guò)返回的這個(gè)方法。從中找到這些類(lèi)并一一執(zhí)行測(cè)試。
先以一個(gè)大牛的一段關(guān)于Python Metapgramming的著名的話來(lái)做開(kāi)頭:
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%的用戶都無(wú)需費(fèi)神的黑科技。如果你還在糾結(jié)你是不是需要它的話,答案是NO (真正需要的人根本不需要解釋?zhuān)?- Tim Peters
這是什么鬼話?道可道,非常道嗎?
Meta?好,裝B已畢。這確實(shí)是一個(gè)冷僻的,不常用的話題。一篇短文肯定講不完。 所以叫做初探。
英文meta這個(gè)詞其實(shí)是從希臘語(yǔ)里面借來(lái)的。wikipedia上的解釋是:
indicate a concept which is an abstraction behind another concept, used to complete or add to the latter
不看還好,其實(shí)看了更暈。好在后面的解釋有一句“更高一層的抽象”,可以幫助理解。 其實(shí)我們可以這樣理解。meta的意思就是“關(guān)于什么的什么”:比如metadata可以理解為“關(guān)于數(shù)據(jù)的數(shù)據(jù)”,metaprogramming可以理解為“關(guān)于編程的編程”。這就和“更高一層的抽象” 比較契合了。同時(shí)又隱隱和編程中的另一個(gè)永恒主題-recursion聯(lián)系在了一起。
另外,meta這個(gè)詞天朝這邊翻譯成“元”,海峽對(duì)岸翻譯成“后設(shè)”。其實(shí)我都不大理解從何而來(lái)。
實(shí)例聚焦到我們今天的主題,metaprogramming就是編寫(xiě)用來(lái)生成代碼的代碼。
假設(shè)我們寫(xiě)了一個(gè)NB的函數(shù),用來(lái)計(jì)算一個(gè)任意復(fù)雜的算數(shù)表達(dá)式的值:
像1+2, 3*6+10, 什么的都可以交給它去計(jì)算。這樣的函數(shù)的算法不是我們的主題,所以我們請(qǐng)出python自帶的大招eval(),一行就可以搞定了:
def calc(expression): return eval(expression)
因?yàn)檩斎氲目赡苄允菬o(wú)限的,所以我們肯定要好好測(cè)試一下這個(gè)函數(shù)了。假定我們想了 上百個(gè)test case。又假定我們是用unittest這個(gè)module來(lái)做測(cè)試的。這樣的測(cè)試程序一般會(huì)長(zhǎng)成這樣:
import unittest class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual("foo".upper(), "FOO") def test_isupper(self): self.assertTrue("FOO".isupper()) self.assertFalse("Foo".isupper()) def test_split(self): s = "hello world" self.assertEqual(s.split(), ["hello", "world"]) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) if __name__ == "__main__": unittest.main()
所以我們的目的就是用metaprogramming的方式來(lái)自動(dòng)產(chǎn)生類(lèi)似上面的測(cè)試類(lèi)。
先上程序后解釋?zhuān)?/p>
#!/usr/bin/python3 import unittest def calc(expression): return eval(expression) def add_test(name, asserts): def test_method(asserts): def fn(self): left, right = asserts.split("=") expected = str(calc(left)) self.assertEqual(expected, right) return fn d = {"test1": test_method(asserts)} cls = type(name, (unittest.TestCase,), d) globals()[name] = cls if __name__ == "__main__": for i, t in enumerate([ "1+2=3", "3*5*6=90"]): add_test("Test%d" % i, t) unittest.main()
NB的calc()函數(shù)我們解釋過(guò)了。main這段也比較簡(jiǎn)單:我們用聲明的方式定義了一組測(cè)試,然后通過(guò)unittest來(lái)執(zhí)行。
有點(diǎn)復(fù)雜的是add_test()。我們先來(lái)看看最內(nèi)層的fn(self)這個(gè)方法。邏輯上,它就是把輸入的測(cè)試用例分成兩份,一份是calc()的輸入,一份是我們期待的結(jié)果;然后調(diào)用calc(), 接著用assertEqual()來(lái)測(cè)試。
但是這個(gè)self有點(diǎn)奇怪 - 這里沒(méi)有類(lèi),哪里來(lái)的self? 其實(shí)fn(self)確實(shí)是一個(gè)類(lèi)的方法,只不過(guò)這個(gè)類(lèi)是我們通過(guò)代碼動(dòng)態(tài)生成的。也就是下面這一行:
cls = type(name, (unittest.TestCase,), d)
這里的type()就是通常我們用來(lái)檢查某個(gè)變量的類(lèi)型的那個(gè)函數(shù)。只不過(guò)它還有另外一種不大為人知的形式:
class type(name, bases, dict)
這第二種形式,就會(huì)產(chǎn)生一個(gè)新的類(lèi)型。以我們的程序?yàn)槔褪且?b>unit.TestCase為baseclass, 產(chǎn)生了一個(gè)名為TestN的新類(lèi)型,改類(lèi)型的實(shí)現(xiàn)由d給出,而d就包含了通過(guò)closure返回的fn(self)這個(gè)方法。只不過(guò)在這個(gè)新類(lèi)里面,它的名字叫做 test1()。
最后,我們把這個(gè)新產(chǎn)生的類(lèi)加入到當(dāng)前全局符號(hào)表里面,也就相當(dāng)于上面給出的unittest的例子。
所以,總結(jié)一下。當(dāng)我們運(yùn)行這個(gè)腳本的時(shí)候,這段比較短的代碼會(huì)針對(duì)每一個(gè)測(cè)試的表達(dá)式產(chǎn)生一個(gè)新的測(cè)試類(lèi),并動(dòng)態(tài)生成測(cè)試的方法加載到該類(lèi)里面。unitest從globals中找到這些類(lèi)并一一執(zhí)行測(cè)試。
上面的例子中,其實(shí)一行一行手打calc(1+2) == 3也沒(méi)什么大不了的。但是當(dāng)你要表達(dá)的邏輯比較復(fù)雜的時(shí)候,metaprogramming的強(qiáng)大就體現(xiàn)出來(lái)了。
總結(jié)那么,看完這篇文章,我們也成為T(mén)im所說(shuō)的1%的程序猿了!其實(shí),也許他的意思是,99%的編程工作都用不到這樣技巧。在一些特殊的場(chǎng)合,比如編寫(xiě)某種框架的時(shí)候,metaprogramming會(huì)做到事半功倍。祝你在實(shí)踐中碰到這樣的機(jī)會(huì)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/40855.html
摘要:但我并不是一個(gè)翻譯者并不會(huì)嚴(yán)格遵守每行每句的翻譯有時(shí)候我會(huì)將表述換個(gè)順序省略一些我認(rèn)為無(wú)關(guān)緊要的話,以便讀者更好理解。類(lèi)也是對(duì)象在理解之前,我們先要掌握中的類(lèi)是什么。這個(gè)對(duì)象類(lèi)自身?yè)碛挟a(chǎn)生對(duì)象實(shí)例的能力。不過(guò)這樣做是為了保持向后兼容性。 前言 這篇博客是我在stackoverflow上看了一個(gè)提問(wèn)回復(fù)后寫(xiě)的,例子基本用的都是e-satis本人的例子,語(yǔ)言組織也基本按照翻譯來(lái)。 但我并不...
摘要:先簡(jiǎn)單介紹下中的元類(lèi)。元類(lèi)就是創(chuàng)建類(lèi)的類(lèi),對(duì)于元類(lèi)來(lái)說(shuō),類(lèi)是它的實(shí)例,將返回。中的所有類(lèi),都是的實(shí)例,換句話說(shuō),是元類(lèi)的基類(lèi)。 我在看源代碼的時(shí)候,經(jīng)常蹦出這一句:How does it work!竟然有這種操作?本系列文章,試圖剖析代碼中發(fā)生的魔法。順便作為自己的閱讀筆記,以作提高。 先簡(jiǎn)單介紹下Python中的元類(lèi)(metaclass)。元類(lèi)就是創(chuàng)建類(lèi)的類(lèi),對(duì)于元類(lèi)來(lái)說(shuō),類(lèi)是它的實(shí)...
摘要:什么是元類(lèi)剛才說(shuō)了,元類(lèi)就是創(chuàng)建類(lèi)的類(lèi)。類(lèi)上面的屬性,相信愿意了解元類(lèi)細(xì)節(jié)的盆友,都肯定見(jiàn)過(guò)這個(gè)東西,而且為之好奇。使用了這個(gè)魔法方法就意味著就會(huì)用指定的元類(lèi)來(lái)創(chuàng)建類(lèi)了。深刻理解中的元類(lèi) (一) python中的類(lèi) 今天看到一篇好文,然后結(jié)合自己的情況總結(jié)一波。這里討論的python類(lèi),都基于python2.7x以及繼承于object的新式類(lèi)進(jìn)行討論。 首先在python中,所有東西都...
摘要:最前面那個(gè),解釋器實(shí)際的流程是解析這段代碼,得知它需要?jiǎng)?chuàng)建一個(gè)類(lèi)對(duì)象,這個(gè)類(lèi)的名字叫做它的父類(lèi)列表用表示是,它的屬性用一個(gè)來(lái)表示就是。解決方法很簡(jiǎn)單關(guān)鍵就是前面被特別標(biāo)記了的應(yīng)當(dāng)返回這個(gè)的父類(lèi)的方法返回的對(duì)象。 (原發(fā)于我的blog:Python: metaclass小記 ) 友情提示:本文不一定適合閱讀,如果執(zhí)意要讀,請(qǐng)備好暈車(chē)藥。 題記 Metaclasses are deepe...
摘要:原鏈接中的元類(lèi)是什么類(lèi)也是對(duì)象在理解元類(lèi)之前,需要掌握中類(lèi)概念。事實(shí)上,是中用于創(chuàng)建所有類(lèi)的元類(lèi)。類(lèi)本身是元類(lèi)的對(duì)象在中,除了,一切皆對(duì)象,一切都是類(lèi)或者元類(lèi)的對(duì)象。事實(shí)上是自己的元類(lèi), 學(xué)習(xí)契機(jī) 項(xiàng)目中使用Elasticsearch(ES)存儲(chǔ)海量業(yè)務(wù)數(shù)據(jù),基于ES向外提供的API進(jìn)一層封裝,按需處理原始數(shù)據(jù)提供更精確、更多樣化的結(jié)果。在研究這一層的代碼時(shí)接觸到@six.add_me...
閱讀 4150·2021-11-23 10:09
閱讀 1409·2021-11-23 09:51
閱讀 3041·2021-11-23 09:51
閱讀 1711·2021-09-07 09:59
閱讀 2437·2019-08-30 15:55
閱讀 2378·2019-08-30 15:55
閱讀 3026·2019-08-30 15:52
閱讀 2628·2019-08-26 17:04