這里只分析幾個(gè)可能會(huì)常用到的魔法方法父晶,想new這種不常用的,用來做元類初始化的或者是init這種初始化使用的 弄跌,每個(gè)人都會(huì)用的就不介紹了甲喝。
其實(shí)每個(gè)魔法方法都是在對(duì)內(nèi)建方法的重寫,和做像裝飾器一樣的行為铛只。理解這個(gè)道理埠胖,嘗試去理解每個(gè)細(xì)節(jié)裝飾器會(huì)比較方便。
1.關(guān)于str和repr:
def __init__(self, world):
self.world = world
def __str__(self):
return 'world is %s str' % self.world
def __repr__(self):
return 'world is %s repr' % self.world
t = Test('world_big')
print str(t)
print repr(t)
output:
world is world_big str
world is world_big repr
其實(shí) str相當(dāng)去是str()方法 而repr相當(dāng)于repr()方法淳玩。str是針對(duì)于讓人更好理解的字符串格式化直撤,而repr是讓機(jī)器更好理解的字符串格式化
其實(shí)獲得返回值的方法也很好測(cè)試,在我們平時(shí)使用python的時(shí)候蜕着,在不適用print直接輸出對(duì)象的時(shí)候谋竖,通常調(diào)用的就是repr方法红柱,這個(gè)時(shí)候改寫repr方法可以讓他方便的輸入我們想要知道的內(nèi)容,而不是一個(gè)默認(rèn)內(nèi)容蓖乘。
2.關(guān)于hash和dir:
其實(shí)在實(shí)際應(yīng)用中锤悄,這兩個(gè)用到的頻率并不高,但是在有些庫(kù)里面是有看到過
hash是hash()方法的裝飾器版本嘉抒,而dir是dir()的裝飾器版本零聚。
class Test(object):
def __init__(self, world):
self.world = world
x = Test('world')
p = Test('world')
print hash(x) == hash(p)
print hash(x.world) == hash(p.world)
class Test2(object):
def __init__(self, song):
self.song = song
def __hash__(self):
return 1241
x = Test2('popo')
p = Test2('janan')
print x, hash(x)
print p, hash(p)
output:
False
True
<__main__.Test2 object at 0x101b0c590> 1241
<__main__.Test2 object at 0x101b0c4d0> 1241
可以看到這里的hash()方法總是會(huì)返回int型的數(shù)字,可以用于比較一個(gè)唯一的對(duì)象些侍,比方說一個(gè)不同內(nèi)存的object不會(huì)相當(dāng)隶症,而相同字符串hash之后就會(huì)相等,然后我們通過修改hash方法來修改hash函數(shù)的行為岗宣,讓他總是返回1241蚂会,也是可以輕松做到的,耗式。
另外一個(gè)方法是dir()颂龙,熟悉python的人都知道dir()可以讓我們查看當(dāng)前環(huán)境下有哪些方法和屬性可以進(jìn)行調(diào)用,如果我們使用dir(object)語法纽什,可以獲得一個(gè)對(duì)象擁有的方法和屬性措嵌,同樣的道理如果我們?cè)陬愔卸x了dir(),就可以指定哪些方法和屬性能夠被dir()方法所查看查找到。
3.關(guān)于控制參數(shù)訪問的getattr芦缰,setattr企巢,delattr,getattribute:
getattr是一旦我們嘗試訪問一個(gè)并不存在的屬性的時(shí)候就是調(diào)用让蕾,而如果這個(gè)屬性存在則不會(huì)調(diào)用該方法浪规。
來看一個(gè)getattr的例子:
def __init__(self, world):
self.world = world
def __getattr__(self, item):
return item
x = Test('world123')
print x.world4
output:
world4
這里我們并沒有world4屬性,在找不到屬性的情況下探孝,正常的繼承object的對(duì)象都會(huì)拋出AtrribuError的錯(cuò)誤笋婿,但是這里我通過getattr魔法方法改變了找不到屬性時(shí)候的類的行為,輸出了查找的屬性的參數(shù)顿颅。
setattr是設(shè)置參數(shù)的時(shí)候會(huì)調(diào)用到的魔方方法缸濒,相當(dāng)于設(shè)置參數(shù)錢的有個(gè)鉤子,每個(gè)設(shè)置屬性的方法都繞不開這個(gè)魔方方法粱腻,只有擁有這個(gè)魔方方法的對(duì)象才可以設(shè)置屬性庇配,在使用這個(gè)方法的時(shí)候要特別注意到不要被循環(huán)調(diào)用了,
下面來看一個(gè)例子:
def __init__(self, world):
self.world = world
def __setattr__(self, name, value):
if name == 'value':
object.__setattr__(self, name, value - 100)
else:
object.__setattr__(self, name, value)
x = Test(123)
print x.world
x.value = 200
print x.value
output:
123
100
這里我們先初始化一個(gè)Test類的實(shí)例x绍些,通過init方法我們可以注意到捞慌,會(huì)給初始化的world參數(shù)進(jìn)行賦值。這里的self.world = world語句就是在做這個(gè)事情柬批。
注意啸澡,這里在進(jìn)行world參數(shù)賦值的時(shí)候袖订,就是會(huì)調(diào)用到setattr方法。這個(gè)例子來看world就是name嗅虏,而后面的值的world就是value著角。我在setattr里面做了一個(gè)行為改寫,我將判斷name 值是'value'的進(jìn)行特殊處理旋恼,把它的value值減少100. 所以輸出了預(yù)期的結(jié)果。
我為什么說setattr特別容易出現(xiàn)循環(huán)調(diào)用奄容?因?yàn)槿魏钨x值方法都會(huì)走這個(gè)魔法方法冰更,如果你在你改寫setattr方法里面使用了類似的賦值,又回循環(huán)調(diào)用回setattr方法昂勒。例如
class Test(object):
def __init__(self, world):
self.world = world
def __setattr__(self, name, value):
self.name = value
x = Test(123)
print x.world
output:
RuntimeError: maximum recursion depth exceeded
這里我們想讓setattr執(zhí)行默認(rèn)行為蜀细,也就是將value賦值給name,和object對(duì)象中的同樣方法戈盈,做類似的操作奠衔。但是這里我們不調(diào)用父類setattr的方法來實(shí)現(xiàn),做這樣的嘗試得到的結(jié)果就是塘娶,超過循環(huán)調(diào)用深度归斤,報(bào)錯(cuò)。因?yàn)檫@里在執(zhí)行初始化方法self.world = world的時(shí)候刁岸,就會(huì)調(diào)用setattr方法脏里,而這里的setattr方法里面的self.name = value又會(huì)調(diào)用自身。所以造成了循環(huán)調(diào)用虹曙。所以使用該魔法方法的時(shí)候要特別注意迫横。
delattr的行為和setattr特別相似,同樣需要注意的也是循環(huán)調(diào)用問題酝碳,其他都差不多矾踱,只是把屬性賦值變成了 del self.name這樣的表示。下面直接上個(gè)例子疏哗,不再多贅述呛讲。
def __init__(self, world):
self.world = world
def __delattr__(self, item):
print 'hahaha del something'
object.__delattr__(self, item)
x = Test(123)
del x.world
print x.world
output:
hahaha del something
Traceback (most recent call last):
File "/Users/piperck/Desktop/py_pra/laplace_pra/practie_01_23/c2.py", line 12, in <module>
print x.world
AttributeError: 'Test' object has no attribute 'world'
可以看到我們將屬性刪除之后,就找不到那個(gè)屬性了返奉。但是在刪除屬性的時(shí)候調(diào)用了delattr圣蝎,我在里面打印了一段話,在執(zhí)行之前被打印出來了
getattribute和getattr方法唯一不同的地方是衡瓶,上面我們已經(jīng)介紹了getattr方法只能在找不到屬性的時(shí)候攔截調(diào)用徘公,然后進(jìn)行重載或者加入一些其他操作。但是getattribute更加強(qiáng)大哮针,他可以攔截所有的屬性獲取关面。所以也容易出現(xiàn)我們上面提到的坦袍,循環(huán)調(diào)用的問題。下面上一個(gè)例子來說明這個(gè)問題:
class Test(object):
def __init__(self, world):
self.world = world
def __getattribute__(self, item):
print 'get_something: %s' % item
return item
x = Test(123)
print x.world
print x.pp
output:
get_something: world
world
get_something: pp
pp
可以看到等太,區(qū)別于getattr只攔截不存在的屬性捂齐,getattribute會(huì)攔截所有的屬性。所以導(dǎo)致了已經(jīng)被初始化的world值123缩抡,也被改寫成了字符串world奠宜。而不存在的屬性也被改寫了成了pp。