一、魔法方法特點
- 被雙上下滑線包圍
- 魔法方法是物件導向的Python的一切,如果你不知道魔法方法,說明你還沒能意識到物件導向的Python的強大(不是說Python指令碼)
- 通過對制定方法的重寫,完全可以讓python根據個人的用途去實現!
二、魔法方法
1、構造相關
__init__(self [,...])構造方法
- 不寫時,會預設存在一個無參的,寫了會覆蓋
- 必須返回None,不能寫 return,否則返回TypeError;因此不要對init做返回;
__new__(cls[, ...])
- 物件例項化時呼叫的第一個方法,在init之前,它有個很大的不同第一個引數不是self,而是這個類cls,返回一個物件,cls後邊的引數會原封不動的傳給__init__()方法
- 平時極少重寫,當繼承一個不可變型別又需要修改的時候,需要重寫new
class CapStr(str): #繼承str類是不可變得
def __new__(cls, string): #重寫new,第一個傳入class,叫其他名字也無所謂,只是為了區分
string = string.upper() #全部變大寫
return str.__new__(cls, string) 變完大寫後,把它作為引數去呼叫基類的new方法
a = CapStr('I love Money') #得到的a為大寫
__del__(self)析構
- delete縮寫,當沒有任何變數引用這個變數後,垃圾回收機制會自動銷燬時呼叫它
- 並非執行del x 就立馬是呼叫x.__del__();
2、表現類
__str__ 和print()對應
__repr__
>>> class A():
... def __str__(self): #給print()用
... return "hahaha"
...
>>> a = A()
>>> a
<__main__.A object at 0x7f85993317f0>
>>> print(a)
hahaha
列印時,返回字串,需要return 字串,不可以是別的型別!
__str__ = __repr__
>>> class A():
... def __repr__(self): #給obj物件用
... return "hahaha"
...
>>> a = A()
>>> a
hahaha
>>> print(a)
hahaha
>>>
3、有關算數運算的魔法方法
在python2.2之前類和型別是分開的;類是屬性和方法的封裝;型別是像整型、浮點型、字串等這些;但Python2.2之後,對二者進行統一,做法是將int、float、string、list、tuple等這些bif統統轉化為工廠函式
int('123')在以前是呼叫int函式,把引數轉化為整型;現在是用引數例項化一個int物件。並且物件可以做加法!
呼叫__add__(),因此可以重寫,但重寫沒有多大意義吧
下邊寫法會進入無線遞迴:
改進:
推薦第一種寫法
上表中的魔法方法加上r,就是反運算子:a + b,如果a物件的add方法沒有實現或者不支援相應的操作的時候,那麼python就會自動找到物件b的__radd__(self, other)方法
1為什麼沒有__add__()方法??
在減法中,為了實現目的,可以修改為 def __radd__(self, other): return int.__sub__(other, self)
一元操作符
__neg__(self) 定義正號的行為:+x
__pos__(self) 定義符號的行為:-x
__abs__(self) 定義當被abs()呼叫時的行為
__invert__(self) 定義按位取反的行為:~x
4、屬性訪問
點;bif;
__getattr__(self, name)
- 定義當使用者試圖獲取一個不存在的屬性時的行為
__getattribute__(self, name)
- 定義當該類的屬性被訪問時的行為
__setattr__(self, name, value)
- 定義當一個屬性被設定時的行為
__delattr__(self, name)
- 定義當一個屬性被刪除時的行為
>>> 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 1 >>> del c.x delattr
定義一個矩形類,且修改__setattr__()方法,如果為一個叫square的屬性賦值,那麼說明這是一個正方形,值就是正方形的邊長,寬和高等於邊長。
1 class Rextangle(): 2 def __init__(self, width=0, height=0): 3 self.width = width 4 self.height = height 5 6 def __setattr__(self, name, value): 7 if name == 'square': 8 self.width = value 9 self.height = value 10 else: 11第一種 self.width = value #報錯,無限遞迴 12 self.height = value #報錯,無限遞迴
第二種 self.name = value #報錯,無限遞迴
第三種 super().__setattr__(name, value) #正確(推薦)
第四種 self.__dict__[name] = value #正確 13 def getArea(self): 14 return self.width * self.height
r = Rectangle(4, 5) 初始化是,有self.width\self.height賦值,回去呼叫被改寫了的__setattr__()方法
r.getArea() 返回20
r.square = 10
r.width r.height 返回10
r.getArea() 返回100
r.__dict__ 返回{'width':10, 'height':10} 因此會有“第四種”
5、描述符
描述符就是將某種特殊型別的類的例項指派給另一個類的屬性。
- __get__(self, instance, owner) 用於訪問屬性時呼叫,它返回屬性值
- __set__(self, instance, value) 將在屬性分配操作中呼叫,不返回任何內容
- __delete__(self, instance) 控制刪除操作,不返回任何內容
這三個方法跟4裡的屬性訪問方法很相似:python中__get__,__getattr__,__getattribute__的區別:主要看例子
1 class MyDecriptor(): 2 def __get__(self, instance, owner): 3 print("getting...", self, instance, owner) 4 5 def __set__(self, instance, value): 6 print("setting...", self, instance, value) 7 8 def __delete__(self, instance): 9 print("deleting...", self, instance) In [51]: class Test(): ...: x = MyDecriptor() #將類的例項指派給Test類的屬性x,此時MyDecriptor()例項裡的屬性、方法就是x的描述符 ...: In [52]: test = Test() #例項化Test類 In [53]: test.x #呼叫了描述符的__get__() getting... <yu412.MyDecriptor object at 0x7fa160356dd8> <__main__.Test object at 0x7fa1602a4f98> <class '__main__.Test'> In [54]: test #例項 Out[54]: <__main__.Test at 0x7fa1602a4f98> In [55]: Test #類 Out[55]: __main__.Test In [56]: test.x = "X-man" #呼叫__set__() setting... <yu412.MyDecriptor object at 0x7fa160356dd8> <__main__.Test object at 0x7fa1602a4f98> X-man In [57]: del test.x #呼叫__delete__() deleting... <yu412.MyDecriptor object at 0x7fa160356dd8> <__main__.Test object at 0x7fa1602a4f98>
利用以上原理可以輕鬆實現property()方法
練習:
先定義一個溫度類,然後定義兩個描述符類用於描述攝氏度和華氏度兩個屬性;要求兩個屬性會自動進行轉換,也就是說你可以給攝氏度這個屬性賦值,然後列印的華氏度屬性是自動轉換後的結果
6、定製容器
http://www.cnblogs.com/daduryi/p/6737186.html
_len__()、__getitem__()、__setitem__()和__delitem__()
__iter__()——返回迭代器本身
__next__()——決定迭代器的規則