70.魔法方法:http://bbs.fishc.com/forum.php?mod=viewthread&tid=48793&highlight=%C4%A7%B7%A8%B7%BD%B7%A8
魔法方法總是被雙下劃線包圍距帅,例如__init__
魔法方法的‘魔力’體現(xiàn)再他們總能再適當(dāng)?shù)臅r候被自動調(diào)用
1)__init__(self[,...])類在實例化對象的時候篡殷,首先會調(diào)用的一個方法
有時候在類定義是寫__init__方法猜年,有時候卻沒有奥溺,這是為什么呢?
>>> class Rectangle:
???def __init__(self,x,y):
??????self.x=x
??????self.y=y
???def getPeri(self):
??????return (self.x+self.y)*2
???def getArea(self):
??????return self.x*self.y
>>> rect=Rectangle(3,4)
>>> rect.getPeri()
14
>>> rect.getArea()
12
#__init__不能有返回值
>>> class A:
???def __init__(self):
??????return 'haha'
>>> a=A()
Traceback (most recent call last):
?File "", line 1, in
???a=A()
TypeError: __init__() should return None, not 'str'
2)__new__(cls[,...]) 實際上在實例化的時候先自動調(diào)用了這個魔法方法迎膜,之后才自動調(diào)用了__init__魔法方法
該魔法方法的參數(shù)是這個類
這個方法的返回值是一個實例對象牡借,通常返回cls這個類的實例對象混弥,也可以返回其他類的對象
這個new方法一般很少去重寫,一般讓python用默認(rèn)的方法去執(zhí)行就行
當(dāng)繼承一個不可變類型但需要進(jìn)行修改需要重寫這個方法
>>> class CapStr(str):
???def __new__(cls,string):
??????string=string.upper()
??????return str.__new__(cls,string)
>>> a=CapStr('hahah') #由于CapStr這個類是繼承了str類荐吉,str是不可改變的類焙糟,那么就不能修改str的__init__方法進(jìn)行修改,所以應(yīng)該在重寫__new__样屠,增加了將字符串轉(zhuǎn)化成大寫然后再調(diào)用父類str的__new__的方法
>>> a
'HAHAH'
3)__del__(self)析構(gòu)方法穿撮,當(dāng)對象被銷毀時候自動調(diào)用(當(dāng)垃圾回收機(jī)制自動銷毀的時候:實例化對象都沒有引用了的時候)
>>> class C:
???def __init__(self):
??????print('我是__init__方法,我被調(diào)用了')
???def __del__(self):
??????print('我是__del__方法痪欲,我被調(diào)用了')
>>> c=C()
我是__init__方法悦穿,我被調(diào)用了
>>> c1=c
>>> c2=c1
>>> c3=c2
>>> c4=c3
>>> del c1
>>> del c
>>> del c2
>>> del c3
>>> del c4
我是__del__方法叙赚,我被調(diào)用了
4)算數(shù)魔法方法當(dāng)你的對象進(jìn)行了算數(shù)操作的時候歹嘹,以下魔法方法就會自動被調(diào)用,這些方法可以被重寫
__add__(self呀伙,other):定義加法行為+
__sub__(self,other):定義減法行為-
>>> class New_int(int):
???def __add__(self,other):
??????return int.__sub__(self,other)
???def __sub__(self,other):
??????return int.__add__(self,other)
>>> a=New_int(3)
>>> b=New_int(5)
>>> a+b
-2
__mul__(self,other):定義乘法行為*
__truediv__(self,other):定義真除法行為/
__floordiv__(self,other):定義整數(shù)除法行為://
__mod__(self,other):定義取模算法的行為:%
__divmod__(self,other):定義當(dāng)被divmod()調(diào)用時的行為
__pow__(self,other):定義當(dāng)被power()調(diào)用或**運算時的行為
__lshift__(self,other):定義按位左移位的行為:<<
__rshift__(self,other):定義按右移位的行為>>
__and__(self,other):定義按位與操作的行為&
__xor__(self,other):定義按位異或操作的行為^
__or__(self,other):定義按位或操作的行為|
【反運算魔法方法(上面的所有魔法方法前面加上r)】當(dāng)a+b蛙埂,a是一個數(shù)值欲账,那么不能利用a的魔法方法罢洲,只用利用b的r魔法方法殿较,反魔法方法也可以改寫
>>> class Nint(int):
???def __radd__(self,other):
??????return int.__sub__(self,other)
>>> a=Nint(5)
>>> b=Nint(3)
>>> a+b
2
>>> 1+b
2
【增量運算符】a+=1 上面所有的魔法方法前面家i
【一元操作符】-a
5)案例練習(xí)
定制一個計時器的類
start和stop方法代表啟動計時和停止計時
假設(shè)計時器對象t1洽瞬,print(t1)和直接調(diào)用t1均顯示結(jié)果
當(dāng)計時器未啟動或已經(jīng)停止計時为障,調(diào)用stop方法會給予溫馨提示
兩個計時器對象可以進(jìn)行相加:t1+t2
只能使用提供的有限資源完成
資源如下:
使用time模塊的localtime方法獲取當(dāng)前時間
time.localtime返回struct_time的時間格式(time模塊詳解http://bbs.fishc.com/forum.php?mod=viewthread&tid=51326&highlight=time%2B%C4%A3%BF%E9%CF%EA%BD%E2
表現(xiàn)你的類:__str__和__repr__:由于要求輸入t1回車顯示結(jié)果(__repr__)和print(t1)(__str__)顯示結(jié)果果則需要重寫這倆魔法函數(shù)
__str__
>>> class A:
???def __str__(self):
??????return 'hahah'
>>> a=A()
>>> print(a) #__str__就是當(dāng)要print出來一個實例化對象的時候要自動調(diào)用的魔法方法
hahah
__repr__
>>> class B:
???def __repr__(self):
??????return 'haha'
>>> b=B()
>>> b #__repr__就是當(dāng)要對實例化對象直接敲回車的時候要自動調(diào)用的魔法方法
haha
[1]編寫代碼模塊MyTime.py
import time as t
class MyTimer():
???def __init__(self):
???????self.unit=['年','月','日','時','分','秒']
???????self.prompt= '未開始計時'
???????self.begin=0
???????self.end=0
???????self.lasted=[]
???def __str__(self):
???????return self.prompt
???__repr__=__str__
???def __add__(self,other):
???????prompt='總共運行了' #局部變量
???????result=[]
???????for index in range(6):
???????????result.append(self.lasted[index]+other.lasted[index])
???????????if result[index]:
???????????????prompt+=(str(result[index])+self.unit[index])
???????return prompt
???#開始計時
???def start(self):
???????self.begin=t.localtime()
???????self.prompt='請先stop'
???????print('計時開始。。')
???#停止計時
???def stop(self):
???????if not self.begin:
???????????print('請先start')
???????else:
???????????self.end=t.localtime()
???????????self._calc()
???????????print('計時結(jié)束')
???#內(nèi)部方法伴逸,計算運行時間
???def _calc(self):
???????self.lasted=[]
???????self.prompt= '總過運行了'
???????for index in range(6):
???????????self.lasted.append(self.end[index]-self.begin[index])
???????????if self.lasted[index]:
???????????????self.prompt+=(str(self.lasted[index])+self.unit[index])
???????#為下一輪初始化變量
???????self.begin=0
???????self.end=0
[2]運行代碼模塊
>>>
===================== RESTART: D:/python/3.5.1/Mytime.py =====================
>>> t1=MyTimer()
>>> t1
未開始計時
>>> t1.stop()
請先start
>>> t1.start()
計時開始顷锰。州藕。
>>> t1.start()
計時開始笨枯。。
>>> t1
請先stop
>>> t1.stop()
計時結(jié)束
>>> t1
總過運行了15秒
>>> t2=MyTimer()
>>> t2.start()
計時開始遇西。馅精。
>>> t2.stop()
計時結(jié)束
>>> t2
總過運行了5秒
>>> t1+t2
'總共運行了20秒'
>>>
6)魔法方法對于屬性訪問的用用
__getattr__(self,name):定義當(dāng)用戶試圖獲取一個不存在的屬性時候會被自動調(diào)用的方法
__getattribute__(self,name):定義當(dāng)該類的屬性被訪問的時候會自動調(diào)用的方法
__setatter__(self,name,value):定義當(dāng)一個屬性被設(shè)置時候,會自動調(diào)用的方法
__delattr__(self,name):定義當(dāng)一個屬性被刪除時候會自動調(diào)用的方法
>>> class C:
???def __getattribute__(self,name):
??????print('getattribute')
??????return super().__getattribute(name)
???def __getattr__(self,name):
??????print('getattr')
???def __setattr__(self,name,value):
??????print('setattr')
??????super().__setattr__(name,value)
???def __delattr__(self,name):
??????print('delattr')
??????super().__delattr__(name)
>>> c=C()
>>> c.x
getattribute
getattr
>>> c.x=1
setattr
>>> c.x
getattribute
getattr
>>> del c.x
delattr
#以下例子會造成改造魔法方法后的死循環(huán)
class Rectangle:
???def __init__(self,width=0,height=0):
???????self.width=width #既然發(fā)生賦值操作就會自動除法__setattr__但是這個魔法方法被改造后里頭又有一個賦值粱檀,所以會死循環(huán)
???????self.height=height
???def __setattr__(self,name,value):
???????if name=='square':
???????????self.width=value
???????????self.height=value
???????else:
???????????self.name=value
???def getAre(self):
???????return self.width*self.height
7)描述符:將某種特殊類型的類的實例指派給另一個類的屬性
特殊類型是實現(xiàn)以下三個方法一個或者多個的類就是特殊類型的類
__get__(self,instance,owner)用于訪問屬性洲敢,它返回屬性的值
__set__(self,instance,value)將再屬性分配操作中調(diào)用,不返回任何內(nèi)容
__delete__(self,instance)控制刪除操作茄蚯,不返回任何內(nèi)容
>>> class MyDecriptor: #這個類含有上述三個方法
???def __get__(self,instance,owner):
??????print('getting...',self,instance,owner)
???def __set__(self,instance,value):
??????print('setting...',self,instance,value)
???def __delete__(self,instance):
??????print('deleting...',self,instance)
>>> class Test:
???x=MyDecriptor() #把MyDecriptor這個類的實例MyDecriptor() 賦值給 Test類的屬性x压彭,那么這個MyDecriptor類就是描述符類
>>> test=Test()
>>> test.x? #調(diào)用MyDecriptor的__get__方法
getting... <__main__.MyDecriptor object at 0x0000012E3FF4D748> <__main__.Test object at 0x0000012E3FF4D898>
>>> test
<__main__.Test object at 0x0000012E3FF4D898>
>>> Test
>>> test.x='x-man' #調(diào)用MyDecriptor的__set__方法
setting... <__main__.MyDecriptor object at 0x0000012E3FF4D748> <__main__.Test object at 0x0000012E3FF4D898> x-man
>>> del test.x? #調(diào)用MyDecriptor的__del__方法
deleting... <__main__.MyDecriptor object at 0x0000012E3FF4D748> <__main__.Test object at 0x0000012E3FF4D898>
以下是編寫自己的property類
>>> class Myproperty:
???def __init__(self,fget=None,fset=None,fdel=None):
??????self.fget=fget
??????self.fset=fset
??????self.fdel=fdel
???def __get__(self,instance,owner):
??????return self.fget(instance)
???def __set__(self,instance,value):
??????self.fset(instance,value)
???def __del__(self,instance):
??????self.fdel(instance)
>>> class C:
???def __init__(self):
??????self._x=None
???def getx(self):
??????return self._x
???def setx(self,value):
??????self._x=value
???def delx(self):
??????del self._x
???x=Myproperty(getx,setx,delx)
>>> c=C()
>>> c.x='haha'
>>> c.x
'haha'
>>> x._x
Traceback (most recent call last):
?File "", line 1, in
???x._x
NameError: name 'x' is not defined
>>> c,_x
Traceback (most recent call last):
?File "", line 1, in
???c,_x
NameError: name '_x' is not defined
>>> c._x
'haha'
#以下是一個溫度計數(shù)值轉(zhuǎn)化的練習(xí)
先定義一個溫度類,然后定義兩個描述符類喲關(guān)于描述攝氏度和華氏度兩個屬性
要求兩個屬性會自動進(jìn)行轉(zhuǎn)換渗常,也就是說你可以給攝氏度這個屬性賦值壮不,然后打印的華氏度屬性是自動轉(zhuǎn)換后的結(jié)果
class Celsius:
???def __init__(self,value=26.0):
???????self.value=float(value)
???def __get__(self,instance,owner):
???????return self.value
???def __set__(self,instance,value):
???????self.value=float(value)
class Fahrenheit:
???def __get__(self,instance,owner):
???????return instance.cel*1.8+32
???def __set__(self,instance,value):
???????instance.cel=(float(value)-32)/1.8
class Temperature:
???cel=Celsius()
???fah=Fahrenheit()
調(diào)用后
>>>
====================== RESTART: D:/python/3.5.1/temp.py ======================
>>> temp=Temperature()#實例化對象此時調(diào)用了__init__方法,使得temp的value=26.0
>>> temp.cel? #這個操作會觸發(fā)Celsius描述符的__get__方法皱碘,返回對象temp的value
26.0
>>> temp.cel=30 #這個操作會觸發(fā)Celsius描述符的__set__方法询一,將對象的value修改成30
>>> temp.fah #這個操作會觸發(fā)Fahrenheit描述符的__get__方法
86.0
>>> temp.fah=100 #這個操作會觸發(fā)Fahrenheit描述符的__set__方法
>>> temp.cel
37.77777777777778
>>>
8)定制序列
協(xié)議與其他編程語言中的接口很相似,他對丁你哪些方法必須要定義
容器類型的協(xié)議
如果說希望定制的容器是不可變的話癌椿,你只需要定義__len__()和__getitem__()方法
如果說你希望定制的容器是可變的話健蕊,除了__len__()和__getitem__()方法,你還需要定義__setitem__()和__delitem__()兩個方法
編寫一個不可改變的自定義列表踢俄,要求記錄列表中每個元素被訪問的次數(shù)
class Countlist:
???def __init__(self,*args):
???????self.values=[x for x in args]
???????self.count={}.fromkeys(range(len(self.values)),0)
???def __len__(self):
???????return len(self.values)
???def __getitem__(self,key):
???????self.count[key]+=1
???????return self.values[key]
運行腳本
>>>
=================== RESTART: D:/python/3.5.1/Countlist.py ===================
>>> c1=Countlist(1,3,5,7,9) #調(diào)用__init__方法
>>> c2=Countlist(2,4,6,8,10)
>>> c1[1] #調(diào)用__getitem__方法
3
>>> c2[1]
4
>>> c1[1]+c2[1]
7
>>> c1.count
{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
9)迭代器 http://bbs.fishc.com/forum.php?mod=viewthread&tid=56023&highlight=yield
iter()? 對一個容器對象調(diào)用該bif可以得到一個迭代器
next() 對一個迭代器調(diào)用該big可以返回一個值
當(dāng)?shù)鳑]有值返回系統(tǒng)會返回報錯
>>> string='fishc'
>>> it=iter(string)
>>> next(it)
'f'
>>> next(it)
'i'
>>> next(it)
's'
>>> next(it)
'h'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
?File "", line 1, in
???next(it)
StopIteration
用這里的兩個bif實現(xiàn)for的作用
>>> string='fishc'
>>> it=iter(string)
>>> while True:
???try:
??????each=next(it)
???except StopIteration:
??????break
???print(each)
f
i
s
h
c
#利用__iter__() 和 __next__()方法
>>> class Fibs:
???def __init__(self,n=10):
??????self.a=0
??????self.b=1
??????self.n=n
???def __iter__(self):
??????return self
???def __next__(self):
??????self.a,self.b=self.b,self.a+self.b
??????if self.a>self.n:
?????????raise StopIteration
??????return self.a
>>> fibs=Fibs()
>>> for each in fibs:
???print(each)
1
1
2
3
5
8