這里只分析幾個可能會常用到的魔法方法,像__new__這種不常用的,用來做元類初始化的或者是__init__這種初始化使用的 每個人都會用的就不介紹了,
其實每個魔法方法都是在對內建方法的重寫,和做像裝飾器一樣的行為,理解這個道理 再嘗試去理解每個細節裝飾器會比較方便,
關于__str__和__repr__:
直接上例子:
class Test(object):
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 strworld is world_big repr
其實__str__相當于是str()方法 而__repr__相當于repr()方法,str是針對于讓人更好理解的字串格式化,而repr是讓機器更好理解的字串格式化,
其實獲得回傳值的方法也很好測驗,在我們平時使用ipython的時候,在不使用print直接輸出物件的時候,通常呼叫的就是repr方法,這個時候改寫repr方法可以讓他方便的輸出我們想要知道的內容,而不是一個默認內容,
關于__hash__和__dir__:
其實在實際應用中寫了這么久python,也沒有用到需要這兩個方法出現的地方,但是在有些庫里面是有看到過,
__hash__是hash()方法的裝飾器版本,而__dir__是dir()的裝飾器版本,
上代碼展示一下__hash__用法:
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:
FalseTrue<__main__.Test2 object at 0x101b0c590> 1241<__main__.Test2 object at 0x101b0c4d0> 1241
復制代碼
可以看到這里的hash()方法總是會回傳int型的數字,可以用于比較一個唯一的物件,比方說一個不同記憶體的object不會相當,而相同字串hash之后就會相等,然后我們通過修改__ hash__方法來修改hash函式的行為,讓他總是回傳1241,也是可以輕松做到的,
另外一個方法是dir(),熟悉python的人都知道dir()可以讓我們查看當前環境下有哪些方法和屬性可以進行呼叫,如果我們使用dir(object)語法,可以獲得一個物件擁有的方法和屬性,
同樣的道理如果我們在類中定義了__dir__(),就可以指定哪些方法和屬性能夠被dir()方法所查看查找到,道理一樣我這里不再貼出代碼了,有興趣的朋友可以自己去試試,
關于控制引數訪問的__getattr__, setattr, delattr, getattribute:
__getattr__是一旦我們嘗試訪問一個并不存在的屬性的時候就會呼叫,而如果這個屬性存在則不會呼叫該方法,
來看一個__getattr__的例子:
class Test(object):
def __init__(self, world):
self.world = world
def __getattr__(self, item):
return item
x = Test('world123')
print x.world4output:world4
這里我們并沒有world4屬性,在找不到屬性的情況下,正常的繼承object的物件都會拋出AtrributeError的錯誤,但是這里我通過__getattr__魔法方法改變了找不到屬性時候的類的行為,輸出了查找的屬性的引數,
__setattr__是設定引數的時候會呼叫到的魔法方法,相當于設定引數前的一個鉤子,每個設定屬性的方法都繞不開這個魔法方法,只有擁有這個魔法方法的物件才可以設定屬性,在使用這個方法的時候要特別注意到不要被回圈呼叫了,
下面來看一個例子:
'''
遇到問題沒人解答?小編創建了一個Python學習交流群:778463939
尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書!
'''
class Test(object):
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 = https://www.cnblogs.com/xxpythonxx/p/200
print x.valueoutput:123100
這里我們先初始化一個Test類的實體x,通過__init__方法我們可以注意到,會給初始化的world引數進行賦值,這里的self.world = world陳述句就是在做這個事情,
注意,這里在進行world引數賦值的時候,就是會呼叫到__setattr__方法,這個例子來看world就是name,而后面的值的world就是value,我在__setattr__里面做了一個行為改寫,我將判斷name 值是'value'的進行特殊處理,把它的value值減少100. 所以輸出了預期的結果,
我為什么說__setattr__特別容易出現回圈呼叫?因為任何賦值方法都會走這個魔法方法,如果你在你改寫__setattr__方法里面使用了類似的賦值,又回回圈呼叫回__setattr__方法,例如:
class Test(object):
def __init__(self, world):
self.world = world
def __setattr__(self, name, value):
self.name = value
x = Test(123)
print x.worldoutput:RuntimeError: maximum recursion depth exceeded
這里我們想讓__setattr__執行默認行為,也就是將value賦值給name,和object物件中的同樣方法,做類似的操作,但是這里我們不呼叫父類__setattr__的方法來實作,做這樣的嘗試得到的結果就是,超過回圈呼叫深度,報錯,因為這里在執行初始化方法self.world = world的時候,就會呼叫__setattr__方法,而這里的__setattr__方法里面的self.name = value又會呼叫自身,所以造成了回圈呼叫,所以使用該魔法方法的時候要特別注意,
__delattr__的行為和__setattr__特別相似,同樣需要注意的也是回圈呼叫問題,其他都差不多,只是把屬性賦值變成了 del self.name這樣的表示,下面直接上個例子,不再多贅述,
class Test(object):
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.worldoutput:
hahaha del somethingTraceback (most recent call last):File "/Users/piperck/Desktop/py_pra/laplace_pra/practie_01_23/c2.py", line 12, in <module>print x.worldAttributeError: 'Test' object has no attribute 'world'
可以看到我們將屬性洗掉之后,就找不到那個屬性了,但是在洗掉屬性的時候呼叫了__delattr__,我在里面列印了一段話,在執行之前被列印出來了
__getattribute__和__getattr__方法唯一不同的地方是,上面我們已經介紹了__getattr__方法只能在找不到屬性的時候攔截呼叫,然后進行多載或者加入一些其他操作,但是__getattribute__更加強大,他可以攔截所有的屬性獲取,所以也容易出現我們上面提到的,回圈呼叫的問題,下面上一個例子來說明這個問題:
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.ppoutput:get_something: worldworldget_something: pppp
可以看到,區別于__getattr__只攔截不存在的屬性,__getattribute__會攔截所有的屬性,所以導致了已經被初始化的world值123,也被改寫成了字串world,而不存在的屬性也被改寫了成了pp,
關于__dict__:
先上個例子:
class Test(object):
fly = True
def __init__(self, age):
self.age = age
__dict__魔法方法可以被稱為系統,他是存盤各分層屬性的魔法方法,__dict__中,鍵為屬性名,值為屬性本身,可以這樣理解,在平時我們給類和實體定義的那些屬性,都會被存盤到__dict__方法中用于讀取,而我們平時使用的類似這樣的語法Test.fly 其實就是呼叫了類屬性,同樣可以寫成Test.dict['fly'],除了類屬性,還有實體屬性,當我們用類實體化一個實體,例如上文我們使用p = Test(2)實體化類Test,p也會具有__dict__屬性,這里會輸出:
{'age': 2}
由上可以發現,python中的屬性是進行分層定義的,/object/Test/p這樣一層一層下來的,當我們需要呼叫某個屬性的時候,python會一層一層往上面遍歷上去,先從實體,然后實體的__class__的__dict__,然后是該類的__base__,這樣__dict__一路找上去,如果最后都沒有找到,就拋出AttributeError錯誤,
這里可以延伸一下,沒記錯的話,我前面有篇文章講了一個方法__slot__,__ slots__ 方法就是通過限制__dict__,只讓類實體初始化__slots__里面定義的屬性,而且讓實體不再擁有__dict__方法,來達到節約記憶體的目的,我將會就上面的那個例子重寫一下,來說明這個問題,
'''
遇到問題沒人解答?小編創建了一個Python學習交流群:778463939
尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書!
'''
class Test(object):
__slots__ = ['age']
fly = True
def __init__(self, age):
self.age = age
output:
In [25]: Test.__dict__
Out[25]:
dict_proxy({'__doc__': None,
'__init__': <function __main__.__init__>,
'__module__': '__main__',
'__slots__': ['age'],
'age': <member 'age' of 'Test' objects>,
'fly': True})
In [36]: p.__dict__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-36-3a1cec47d020> in <module>()
----> 1 p.__dict__
AttributeError: 'Test' object has no attribute '__dict__'
In [37]: p.age
Out[37]: 3
In [38]: p.fly
Out[38]: True
可以看到,__slots__方法并沒有阻止由下至上的屬性查找方法,只是不會再允許沒有包含在__slots__陣列中的屬性再被賦值給實體了,但這并不妨礙,繼續呼叫允許訪問的屬性,以及類屬性,
關于__get__, set, del:
在前面的文章里面我也介紹過這三個魔法方法,雖然一般是用不到的,但是在寫庫的時候它們有特別的用途,他們是python另外一個協議descriptor的根基,
同一個物件的不同屬性之間可能存在依賴關系,當某個屬性被修改時,我們希望依賴于該屬性的其他屬性也同時變化,在這種環境下面__dict__方法就無法辦到,因為__dict__方法只能用來存盤靜態屬性,python提供了多種即時生成屬性的方法,其中一種就是property,property是特殊的屬性,比如我們為上面的例子增加一個property特性,使得他能夠動態變化,來看這個例子:
class Test(object):
fly = True
def __init__(self, age):
self.age = age
def whether_fly(self):
if self.age <= 30:
return True
else:
return False
def just_try_try(self, other):
pass
whether_fly = property(whether_fly)
p = Test(20)
print p.age
print p.whether_fly
p.age = 40
print p.age
print p.whether_fly
output:
20
True
40
False
可以看到 我們可以使用這種手段,動態修改屬性值,property有四個引數,前三個引數為函式,分別用于處理查詢特性、修改特性、洗掉特性,最后一個引數為特性的檔案,可以為一個字串,起說明作用,這里我只是要到了第一個引數,查詢的時候動態修改他的回傳值,而第二個引數是在修改值的時候就會體現出來,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/84138.html
標籤:Python
