python的多種魔術(shù)方法

[toc]
定制類和魔法方法

  • new
  • str , repr
  • iter
  • getitem , setitem , delitem
  • getattr , setattr , delattr
  • call

new

在 Python 中,當(dāng)我們創(chuàng)建一個(gè)類的實(shí)例時(shí)淆两,類會(huì)先調(diào)用 new(cls[, ...]) 來創(chuàng)建實(shí)例铺敌,然后 init 方法再對(duì)該實(shí)例(self)進(jìn)行初始化。

關(guān)于 newinit 有幾點(diǎn)需要注意:
new 是在 init 之前被調(diào)用的椅棺;
new 是類方法犁罩,init 是實(shí)例方法;
重載 new 方法两疚,需要返回類的實(shí)例床估;
一般情況下,我們不需要重載 new 方法诱渤。但在某些情況下丐巫,我們想控制實(shí)例的創(chuàng)建過程,這時(shí)可以通過重載 _new 方法來實(shí)現(xiàn)。

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return object.__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self

str & repr

class Foo(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Foo object (name: %s)' % self.name
    def __repr__(self):
        return 'Foo object (name: %s)' % self.name

print Foo('ethan') # 使用 print
Foo object (name: ethan)

str(Foo('ethan')) # 使用 str
'Foo object (name: ethan)'

Foo('ethan') # 直接顯示
<main.Foo at 0x10c37a490>
Foo('ethan') # 使用repr(類中實(shí)現(xiàn))
'Foo object (name: ethan)'

iter

在某些情況下递胧,我們希望實(shí)例對(duì)象可被用于 for...in 循環(huán)碑韵,這時(shí)我們需要在類中定義 iter 和 next(在 Python3 中是 next)方法,其中谓着,iter 返回一個(gè)迭代對(duì)象泼诱,next 返回容器的下一個(gè)元素,在沒有后續(xù)元素時(shí)拋出 StopIteration 異常.

看一個(gè)斐波那契數(shù)列的例子:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):  # 返回迭代器對(duì)象本身
        return self      

    def next(self):      # 返回容器下一個(gè)元素
        self.a, self.b = self.b, self.a + self.b
        return self.a  

fib = Fib()
for i in fib:
... if i > 10:
... break
... print i

getitem赊锚、setitem治筒、delitem

geitem 用于獲取值,類似地舷蒲,setitem 用于設(shè)置值耸袜,delitem 用于刪除值,讓我們看下面一個(gè)例子:

class Point(object):
    def __init__(self):
        self.coordinate = {}

    def __str__(self):
        return "point(%s)" % self.coordinate

    def __getitem__(self, key):
        return self.coordinate.get(key)

    def __setitem__(self, key, value):
        self.coordinate[key] = value

    def __delitem__(self, key):
        del self.coordinate[key]
        print 'delete %s' % key

    def __len__(self):
        return len(self.coordinate)

    __repr__ = __str__

在上面牲平,我們定義了一個(gè) Point 類堤框,它有一個(gè)屬性 coordinate(坐標(biāo)),是一個(gè)字典纵柿,讓我們看看使用:

>>> p = Point()
>>> p['x'] = 2    # 對(duì)應(yīng)于 p.__setitem__('x', 2)
>>> p['y'] = 5    # 對(duì)應(yīng)于 p.__setitem__('y', 5)
>>> p             # 對(duì)應(yīng)于 __repr__
point({'y': 5, 'x': 2})
>>> len(p)        # 對(duì)應(yīng)于 p.__len__
2
>>> p['x']        # 對(duì)應(yīng)于 p.__getitem__('x')
2
>>> p['y']        # 對(duì)應(yīng)于 p.__getitem__('y')
5
>>> del p['x']    # 對(duì)應(yīng)于 p.__delitem__('x')
delete x
>>> p
point({'y': 5})
>>> len(p)
1

getattr蜈抓、setattr、delattr

當(dāng)我們獲取對(duì)象的某個(gè)屬性昂儒,如果該屬性不存在沟使,會(huì)拋出 AttributeError 異常,比如:

class Point(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

>>> p = Point(3, 4)
>>> p.x, p.y
(3, 4)
>>> p.z
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-547-6dce4e43e15c> in <module>()
----> 1 p.z

AttributeError: 'Point' object has no attribute 'z'

那有沒有辦法不讓它拋出異常呢渊跋?當(dāng)然有腊嗡,只需在類的定義中加入 getattr 方法,比如:

class Point(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    def __getattr__(self, attr):
        if attr == 'z':
            return 0

>>> p = Point(3, 4)
>>> p.z
0

現(xiàn)在拾酝,當(dāng)我們調(diào)用不存在的屬性(比如 z)時(shí)燕少,解釋器就會(huì)試圖調(diào)用 getattr(self, 'z') 來獲取值,但是蒿囤,上面的實(shí)現(xiàn)還有一個(gè)問題客们,當(dāng)我們調(diào)用其他屬性,比如 w 蟋软,會(huì)返回 None镶摘,因?yàn)?getattr 默認(rèn)返回就是 None,只有當(dāng) attr 等于 'z' 時(shí)才返回 0岳守,如果我們想讓 getattr 只響應(yīng)幾個(gè)特定的屬性,可以加入異常處理碌冶,修改 getattr 方法湿痢,如下:

def __getattr__(self, attr):
    if attr == 'z':
        return 0
    raise AttributeError("Point object has no attribute %s" % attr)

setattr, delattr

class Point(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __getattr__(self, attr):
        if attr == 'z':
            return 0
        raise AttributeError("Point object has no attribute %s" % attr)

    def __setattr__(self, *args, **kwargs):  
        print 'call func set attr (%s, %s)' % (args, kwargs)
        return object.__setattr__(self, *args, **kwargs)

    def __delattr__(self, *args, **kwargs):  
        print 'call func del attr (%s, %s)' % (args, kwargs)
        return object.__delattr__(self, *args, **kwargs)

>>> p = Point(3, 4)
call func set attr (('x', 3), {})
call func set attr (('y', 4), {})
>>> p.z
0
>>> p.z = 7
call func set attr (('z', 7), {})
>>> p.z
7
>>> p.w
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: Point object has no attribute w
>>> p.w = 8
call func set attr (('w', 8), {})
>>> p.w
8
>>> del p.w
call func del attr (('w',), {})
>>> p.__dict__
{'y': 4, 'x': 3, 'z': 7}

call

我們一般使用 obj.method() 來調(diào)用對(duì)象的方法,那能不能直接在實(shí)例本身上調(diào)用呢?在 Python 中譬重,只要我們?cè)陬愔卸x call 方法拒逮,就可以對(duì)實(shí)例進(jìn)行調(diào)用,比如下面的例子:

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __call__(self, z):
        return self.x + self.y + z

使用如下:

>>> p = Point(3, 4)
>>> callable(p)     # 使用 callable 判斷對(duì)象是否能被調(diào)用
True
>>> p(6)            # 傳入?yún)?shù)臀规,對(duì)實(shí)例進(jìn)行調(diào)用滩援,對(duì)應(yīng) p.__call__(6)
13                  # 3+4+6

slots

在 Python 中,我們?cè)诙x類的時(shí)候可以定義屬性和方法塔嬉。當(dāng)我們創(chuàng)建了一個(gè)類的實(shí)例后玩徊,我們還可以給該實(shí)例綁定任意新的屬性和方法。
看下面一個(gè)簡(jiǎn)單的例子:

class Point(object):    
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

>>> p = Point(3, 4)
>>> p.z = 5    # 綁定了一個(gè)新的屬性
>>> p.z
5
>>> p.__dict__
{'x': 3, 'y': 4, 'z': 5}

在上面谨究,我們創(chuàng)建了實(shí)例 p 之后恩袱,給它綁定了一個(gè)新的屬性 z,這種動(dòng)態(tài)綁定的功能雖然很有用胶哲,但它的代價(jià)是消耗了更多的內(nèi)存畔塔。

因此,為了不浪費(fèi)內(nèi)存鸯屿,可以使用 slots 來告訴 Python 只給一個(gè)固定集合的屬性分配空間澈吨,對(duì)上面的代碼做一點(diǎn)改進(jìn),如下:

class Point(object):
    __slots__ = ('x', 'y')       # 只允許使用 x 和 y

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

上面寄摆,我們給 slots 設(shè)置了一個(gè)元組谅辣,來限制類能添加的屬性。現(xiàn)在冰肴,如果我們想綁定一個(gè)新的屬性屈藐,比如 z,就會(huì)出錯(cuò)了熙尉,如下:

>>> p = Point(3, 4)
>>> p.z = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-648-625ed954d865> in <module>()
----> 1 p.z = 5

AttributeError: 'Point' object has no attribute 'z'

注意:
使用 slots 有一點(diǎn)需要注意的是联逻,slots 設(shè)置的屬性僅對(duì)當(dāng)前類有效,對(duì)繼承的子類不起效检痰,除非子類也定義了 slots包归,這樣,子類允許定義的屬性就是自身的 slots 加上父類的 slots铅歼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末公壤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子椎椰,更是在濱河造成了極大的恐慌厦幅,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慨飘,死亡現(xiàn)場(chǎng)離奇詭異确憨,居然都是意外死亡译荞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門休弃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吞歼,“玉大人,你說我怎么就攤上這事塔猾「萋猓” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵丈甸,是天一觀的道長(zhǎng)糯俗。 經(jīng)常有香客問我,道長(zhǎng)老虫,這世上最難降的妖魔是什么叶骨? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮祈匙,結(jié)果婚禮上忽刽,老公的妹妹穿的比我還像新娘。我一直安慰自己夺欲,他們只是感情好跪帝,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著些阅,像睡著了一般伞剑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上市埋,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天黎泣,我揣著相機(jī)與錄音,去河邊找鬼缤谎。 笑死抒倚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坷澡。 我是一名探鬼主播托呕,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼频敛!你這毒婦竟也來了项郊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤斟赚,失蹤者是張志新(化名)和其女友劉穎着降,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拗军,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹊碍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年厌殉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了食绿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侈咕。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖器紧,靈堂內(nèi)的尸體忽然破棺而出耀销,到底是詐尸還是另有隱情,我是刑警寧澤铲汪,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布熊尉,位于F島的核電站,受9級(jí)特大地震影響掌腰,放射性物質(zhì)發(fā)生泄漏狰住。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一齿梁、第九天 我趴在偏房一處隱蔽的房頂上張望催植。 院中可真熱鬧,春花似錦勺择、人聲如沸创南。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稿辙。三九已至,卻和暖如春气忠,著一層夾襖步出監(jiān)牢的瞬間邻储,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工旧噪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吨娜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓舌菜,卻偏偏與公主長(zhǎng)得像萌壳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子日月,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361