摘要:先從最初的腦洞開始吧。這樣做的目的在于,把調(diào)用過程中的狀態(tài)存儲(chǔ)起來,借此實(shí)現(xiàn)帶狀態(tài)的調(diào)用。這種實(shí)例我們稱之為函數(shù)對(duì)象。在里面也有同樣的機(jī)制。對(duì)于真正的而言,肯定不會(huì)用這種的判斷方式。
有一天閑著無聊的時(shí)候,腦子里突然冒出一個(gè)Magic Method的有趣用法,可以用__getattr__來實(shí)現(xiàn)Python版的method_missing。
順著這個(gè)腦洞想下去,我發(fā)現(xiàn)Python的Magic Method確實(shí)有很多妙用之處。故在此記下幾種有趣(也可能有用的)Magic Method技巧,希望可以拋磚引玉,打開諸位讀者的腦洞,想出更加奇妙的用法。
如果對(duì)Magic Method的了解僅僅停留在知道這個(gè)術(shù)語和若干個(gè)常用方法上(如__lt__,__str__,__len__),可以閱讀下這份教程,看看Magic Method可以用來做些什么。
Python method_missing先從最初的腦洞開始吧。曾幾何時(shí),Ruby社區(qū)的人總是夸耀Ruby的強(qiáng)大的元編程能力,其中method_missing更是不可或缺的特性。通過調(diào)用BaseObject上的method_missing,Ruby可以實(shí)現(xiàn)在調(diào)用不存在的屬性時(shí)進(jìn)行攔截,并動(dòng)態(tài)生成對(duì)應(yīng)的屬性。
Ruby例子
# 來自于Ruby文檔: http://ruby-doc.org/core-2.2.0/BasicObject.html#method-i-method_missing class Roman def roman_to_int(str) # ... end def method_missing(methId) str = methId.id2name roman_to_int(str) end end r = Roman.new r.iv #=> 4 r.xxiii #=> 23 r.mm #=> 2000
method_missing的應(yīng)用是如此地廣泛,以至于只要是成規(guī)模的Ruby庫,多多少少都會(huì)用到它。像是ActiveRecord就是靠這一特性去動(dòng)態(tài)生成關(guān)聯(lián)屬性。
其實(shí)Python一早就內(nèi)置了這一功能。Python有一個(gè)Magic Method叫__getattr__,它會(huì)在找不到屬性的時(shí)候調(diào)用,正好跟Ruby的method_missing是一樣的。
我們可以這樣動(dòng)態(tài)添加方法:
class MyClass(object): def __getattr__(self, name): """called only method missing""" if name == "missed_method": setattr(self, name, lambda : True) return lambda : True myClass = MyClass() print(dir(myClass)) print(myClass.missed_method()) print(dir(myClass))
于是乎,前面的Ruby例子可以改寫成下面的Python版本:
class Roman(object): roman_int_map = { "i": 1, "v": 5, "x": 10, "l": 50, "c":100, "d": 500, "m": 1000 } def roman_to_int(self, s): decimal = 0 for i in range(len(s), 0, -1): if (i == len(s) or self.roman_int_map[s[i-1]] >= self.roman_int_map[s[i]]): decimal += self.roman_int_map[s[i-1]] else: decimal -= self.roman_int_map[s[i-1]] return decimal def __getattr__(self, s): return self.roman_to_int(s) r = Roman() print(r.iv) r.iv #=> 4 r.xxiii #=> 23 r.mm #=> 2000
很有可能你會(huì)覺得這個(gè)例子沒有什么意義,你是對(duì)的!其實(shí)它就是把方法名當(dāng)做一個(gè)羅馬數(shù)字字符串,傳入roman_to_int而已。不過正如遞歸不僅僅能用來計(jì)算斐波那契數(shù)列,__getattr__的這一特技實(shí)際上還是挺有用的。你可以用它來進(jìn)行延時(shí)計(jì)算,或者方法分派,抑或像基于Ruby的DSL一樣動(dòng)態(tài)地合成方法。這里有個(gè)用__getattr__實(shí)現(xiàn)延時(shí)加載的例子。
函數(shù)對(duì)象在C++里面,你可以重載掉operator (),這樣就可以像調(diào)用函數(shù)一樣去調(diào)用一個(gè)類的實(shí)例。這樣做的目的在于,把調(diào)用過程中的狀態(tài)存儲(chǔ)起來,借此實(shí)現(xiàn)帶狀態(tài)的調(diào)用。這種實(shí)例我們稱之為函數(shù)對(duì)象。
在Python里面也有同樣的機(jī)制。如果想要存儲(chǔ)的狀態(tài)只有一種,你需要的是一個(gè)生成器。通過send來設(shè)置存儲(chǔ)的狀態(tài),通過next來獲取調(diào)用的結(jié)果。不過如果你需要存儲(chǔ)多個(gè)不同的狀態(tài),生成器就不夠用了,非得定義一個(gè)函數(shù)對(duì)象不可。
Python里面可以重載__call__來實(shí)現(xiàn)operator ()的功能。下面的例子里面,就是一個(gè)存儲(chǔ)有兩個(gè)狀態(tài)value和called_times的函數(shù)對(duì)象:
class CallableCounter(object): def __init__(self, initial_value=0, start_times=0): self.value = initial_value self.called_times = start_times def __call__(self): print("Call the object and do something with value %d" % self.value) self.value += 1 self.called_times += 1 def reset(self): self.called_times = 0 cc = CallableCounter(initial_value=5) for i in range(10): cc() print(cc.called_times) cc.reset()偽造一個(gè)Dict
最后請(qǐng)?jiān)试S我奉上一個(gè)大腦洞,偽造一個(gè)Dict類。(這個(gè)可就沒有什么實(shí)用價(jià)值了)
首先確定下把數(shù)據(jù)存在哪里。我打算把數(shù)據(jù)存儲(chǔ)在類的__dict__屬性中。由于__dict__屬性的值就是一個(gè)Dict實(shí)例,我只需把調(diào)用在FakeDict上的方法直接轉(zhuǎn)發(fā)給對(duì)應(yīng)的__dict__的方法。代價(jià)是只能接受字符串類型的鍵。
class FakeDict: def __init__(self, iterable=None, **kwarg): if iterable is not None: if isinstance(iterable, dict): self.__dict__ = iterable else: for i in iterable: self[i] = None self.__dict__.update(kwarg) def __len__(self): """len(self)""" return len(self.__dict__) def __str__(self): """it looks like a dict""" return self.__dict__.__str__() __repr__ = __str__
接下來開始做點(diǎn)實(shí)事。Dict最基本的功能是給一個(gè)鍵設(shè)置值和返回一個(gè)鍵對(duì)應(yīng)的值。通過定義__setitem__和__getitem__方法,我們可以重載掉[]=和[]。
def __setitem__(self, k, v): """self[k] = v""" self.__dict__[k] = v def __getitem__(self, k): """self[k]""" return self.__dict__[k]
別忘了del方法:
def __delitem__(self, k): """del self[k]""" del self.__dict__[k]
Dict的一個(gè)常用用途是允許我們迭代里面所有的鍵。這個(gè)可以通過定義__iter__實(shí)現(xiàn)。
def __iter__(self): """it iterates like a dict""" return iter(self.__dict__)
Dict的另一個(gè)常用用途是允許我們查找一個(gè)鍵是否存在。其實(shí)只要定義了__iter__,Python就能判斷if x in y,不過這個(gè)過程中會(huì)遍歷對(duì)象的所有值。對(duì)于真正的Dict而言,肯定不會(huì)用這種O(n)的判斷方式。定義了__contains__之后,Python會(huì)優(yōu)先使用它來判斷if x in y。
def __contains__(self, k): """key in self""" return k in self.__dict__
接下要實(shí)現(xiàn)==的重載,不但要讓FakeDict和FakeDict之間可以進(jìn)行比較,而且要讓FakeDict和正牌的Dict也能進(jìn)行比較。
def __eq__(self, other): """ implement self == other FakeDict, also implement self == other dict """ if isinstance(other, dict): return self.__dict__ == other return self.__dict__ == other.__dict__
要是繼續(xù)實(shí)現(xiàn)了__subclass__和__class__,那么我們的偽Dict就更完備了。這個(gè)就交給感興趣的讀者自己動(dòng)手了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37635.html
摘要:也就是說,如果不需要,兩者使用起來并沒有什么分別。來看個(gè)例子,先定義個(gè)類,里面只有一個(gè)成員方法,返回倍的數(shù)值使用類來掉這個(gè)成員方法使用類來兩者沒有任何區(qū)別,都成功了了成員方法。再看下兩者的區(qū)別因?yàn)槭褂妙悤r(shí),默認(rèn)不會(huì)創(chuàng)建這個(gè)的,所以報(bào)錯(cuò)。 Python的unittest.mock模塊中提供了兩個(gè)主要的mock類,分別是Mock和MagicMock. 先看一下官方文檔的定義: MagicM...
摘要:本篇文章總結(jié)了目前主流的實(shí)現(xiàn)單例模式的方法供讀者參考。使用實(shí)現(xiàn)單例模式同樣,我們?cè)陬惖膭?chuàng)建時(shí)進(jìn)行干預(yù),從而達(dá)到實(shí)現(xiàn)單例的目的。 很多初學(xué)者喜歡用 全局變量 ,因?yàn)檫@比函數(shù)的參數(shù)傳來傳去更容易讓人理解。確實(shí)在很多場(chǎng)景下用全局變量很方便。不過如果代碼規(guī)模增大,并且有多個(gè)文件的時(shí)候,全局變量就會(huì)變得比較混亂。你可能不知道在哪個(gè)文件中定義了相同類型甚至重名的全局變量,也不知道這個(gè)變量在程序的某...
摘要:所以準(zhǔn)確來說是和共同構(gòu)成了構(gòu)造函數(shù)是用來創(chuàng)建類并返回這個(gè)類的實(shí)例而只是將傳入的參數(shù)來初始化該實(shí)例在創(chuàng)建一個(gè)實(shí)例的過程中必定會(huì)被調(diào)用但就不一定,比如通過的方式反序列化一個(gè)實(shí)例時(shí)就不會(huì)調(diào)用。 前言 在Python中,所有以__雙下劃線包起來的方法,都統(tǒng)稱為魔術(shù)方法。比如我們接觸最多的__init__. 有些魔術(shù)方法,我們可能以后一輩子都不會(huì)再遇到了,這里也就只是簡(jiǎn)單介紹下; 而有些魔術(shù)方法...
摘要:關(guān)于的介紹自行查閱官方文檔,這里不再贅述。使用的同學(xué)注意了,如果在我們的代碼中使用到了中相關(guān)的魔術(shù)方法,需要在文件中指明告訴應(yīng)該如何來跟蹤變量屬性。下面我們來具體實(shí)踐分析。確實(shí)這個(gè)樣子可以實(shí)現(xiàn),但沒有利用到這一魔術(shù)方法的特性。 關(guān)于 Magic Methods 的介紹自行查閱官方文檔,這里不再贅述。http://php.net/manual/en/lang... 使用 phpstorm...
摘要:在代碼中可以看到一些常見的,在這里做一個(gè)簡(jiǎn)單的小結(jié)。的妙用在某些場(chǎng)景下我們需要判斷我們是否是從一個(gè)循環(huán)中跳出來的,并且只針對(duì)跳出的情況做相應(yīng)的處理。這時(shí)候我們通常的做法是使用一個(gè)變量來標(biāo)識(shí)是否是從循環(huán)中跳出的。 在 python 代碼中可以看到一些常見的 trick,在這里做一個(gè)簡(jiǎn)單的小結(jié)。 json 字符串格式化 在開發(fā) web 應(yīng)用的時(shí)候經(jīng)常會(huì)用到 json 字符串,但是一段比...
閱讀 966·2019-08-30 14:24
閱讀 1005·2019-08-30 14:13
閱讀 1811·2019-08-29 17:21
閱讀 2707·2019-08-29 13:44
閱讀 1672·2019-08-29 11:04
閱讀 456·2019-08-26 10:44
閱讀 2579·2019-08-23 14:04
閱讀 918·2019-08-23 12:08