python中,用“__”雙下劃線包起來的方法,統(tǒng)稱“魔術(shù)方法”;
比如最常用的init,此方法定義一個對象的初始操作,然而在調(diào)用p = someclass()的時候,在init前面還調(diào)用了一個new方法,兩個共同構(gòu)成了“構(gòu)造函數(shù)”;
1、 init 與 new方法
new用來創(chuàng)建類并返回這個類的實例;init只是將傳入的參數(shù)來初始化該實例。
class Demo:
def __init__(self):
print("如果不在__new__方法里面調(diào)object的__new__方法就不會創(chuàng)建對象,__init__不會被執(zhí)行")
print("如果不在__new__方法里面調(diào)return創(chuàng)建好的對象,__init__不會被執(zhí)行")
def __new__(cls, *args, **kwargs):
print("__new__方法通過調(diào)用object類的__new__方法創(chuàng)建對象,再把對象傳遞給__init__方法")
return super().__new__(cls,*args,**kwargs)
if __name__ == '__main__':
cl = Demo()
# 輸出:
__new__方法通過調(diào)用object類的__new__方法創(chuàng)建對象,再把對象傳遞給__init__方法
如果不在__new__方法里面調(diào)object的__new__方法就不會創(chuàng)建對象,__init__不會被執(zhí)行
如果不在__new__方法里面調(diào)return創(chuàng)建好的對象,__init__不會被執(zhí)行
總結(jié):
1、觸發(fā)方式,實例化類的時候自動調(diào)用;
2、作用,創(chuàng)建類實例;
3、new()方法執(zhí)行順序在init()之前
4、如果不在new方法里面調(diào)object的new方法就不會創(chuàng)建對象,init不會被執(zhí)行;
5、如果不在new方法里面調(diào)return創(chuàng)建好的對象,init不會被執(zhí)行。
2、del方法
在對象生命周期調(diào)用結(jié)束時,del方法會被調(diào)用,可以理解為“構(gòu)析函數(shù)”。一般可以省略
# 實際應(yīng)用
from os.path import join
class Demo:
def __init__(self,filepath='',filename='a.txt'):
''' 給文件進行包裝,從而確認文件在刪除時,進行關(guān)閉 '''
self.file = open(join(filepath,filename),'r+')
def __del__(self):
self.file.close()
del self.file
if __name__ == '__main__':
cl = Demo()
3、call方法
定義了該方法的對象稱為可調(diào)用對象,即該對象可以像函數(shù)一樣被調(diào)用
# __call__方法,可調(diào)用對象callable,自定義函數(shù)、內(nèi)置函數(shù)、類都屬于可調(diào)用對象,即可以把()應(yīng)用在某對象身上稱為可調(diào)用對象
class Demo:
def __init__(self):
print("111")
def __call__(self):
print("222")
if __name__ == '__main__':
cl = Demo()
cl()
print(callable(cl)) # 判斷函數(shù)是否為可調(diào)用函數(shù)用函數(shù)callable
# 輸出:
111
222
True
總結(jié):
1、觸發(fā)方式,將類實例像調(diào)函數(shù)一樣調(diào)用的時候自動調(diào)用;
2、作用讓類實例可以像調(diào)函數(shù)一樣調(diào)用;
3、構(gòu)造方法new的執(zhí)行是由創(chuàng)建對象觸發(fā),即:對象 = 類名();
4、對于call方法的執(zhí)行是由對象后加括號觸發(fā)的,即對象()或類()
實際應(yīng)用
class GDistance:
def __init__(self,g):
self.g = g
def __call__(self,t):
return (self.g*t**2)/2
# 調(diào)用可調(diào)用對象
e_g = GDistance(9.8)
for t in range(3):
print("%d秒 下降 %.2f"%(t, e_g(t)))
# 輸出:
0秒 下降 0.00
1秒 下降 4.90
2秒 下降 19.60
4、自定義容器的magic method
通過字典形式操作屬性值(獲取,設(shè)置,刪除)觸發(fā)以上三個方法。
區(qū)別:"."點的形式操作跟attr相關(guān),"[]"中括號形式操作跟item相關(guān)
首先,實現(xiàn)不可變?nèi)萜鞯脑?,你只能定義 len 和 getitem 。
可變?nèi)萜鲄f(xié)議則需要所有不可變?nèi)萜鞯乃校硗膺€需要 setitem 和 delitem 。
class Demo:
def __init__(self,name):
self.name = name
def __setitem__(self, key, value):
print("執(zhí)行obj[key]=value的時候觸發(fā)__setitem__方法")
self.__dict__[key] = value # 給類中的屬性值分配值,定制特有屬性
def __getitem__(self, item):
print("執(zhí)行obj[key]獲取實例屬性的時候觸發(fā)__getitem__方法")
return self.__dict__[item]
def __delitem__(self, key):
print("調(diào)用delitem")
self.__dict__.pop(key)
if __name__ == '__main__':
c1 = Demo({"name":"tly","age":20})
c1['age'] = 18 # 執(zhí)行setitem
print(c1['name']) # 執(zhí)行g(shù)etitem
del c1['age'] # 執(zhí)行delitem
5、 iter
如果你希望你的對象是可迭代的話,你需要定義 iter 會返回一個迭代器。迭代器必須遵循迭代器協(xié)議,需要有 iter(返回它本身) 和 next