python:類3——魔法方法

daduryi發表於2017-05-08

 一、魔法方法特點

  • 被雙上下滑線包圍
  • 魔法方法是物件導向的Python的一切,如果你不知道魔法方法,說明你還沒能意識到物件導向的Python的強大(不是說Python指令碼)
  • 通過對制定方法的重寫,完全可以讓python根據個人的用途去實現!

 

二、魔法方法

http://bbs.fishc.com/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403


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__()——決定迭代器的規則

 

相關文章