成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

關(guān)于Python Magic Method的若干腦洞

fizz / 3419人閱讀

摘要:先從最初的腦洞開始吧。這樣做的目的在于,把調(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

相關(guān)文章

  • Python中Mock和MagicMock區(qū)別

    摘要:也就是說,如果不需要,兩者使用起來并沒有什么分別。來看個(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...

    TigerChain 評(píng)論0 收藏0
  • Python單例模式(Singleton)N種實(shí)現(xiàn)

    摘要:本篇文章總結(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è)變量在程序的某...

    Maxiye 評(píng)論0 收藏0
  • 介紹Python魔術(shù)方法 - Magic Method

    摘要:所以準(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ù)方法...

    animabear 評(píng)論0 收藏0
  • php magic method 具體應(yīng)用和 phpdoc 結(jié)合

    摘要:關(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...

    csRyan 評(píng)論0 收藏0
  • Python Tricks 若干

    摘要:在代碼中可以看到一些常見的,在這里做一個(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 字符串,但是一段比...

    lovXin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<