Python 定製類的特殊方法與受權

在定製類的過程當中,添加的方法和屬性能完成大部分工做。但若想要類表現出一些特殊行爲或者可以響應某些內建函數或操做符,那麼就須要構建一些特殊方法。這些特殊方法的標識是方法名以雙下劃線(__)開頭與結尾,除了經常使用的構造器 __init__() 外,還有一些經常使用的特殊方法。python

基礎方法:shell

  • C.__new__(self[,arg1,…])        構造器(帶一些可選的參數);一般用在設置不可變數據類型的子類
  • C.__del__(self)        解構器
  • C.__str__(self)        可打印的字符串輸出;內建 str() 及 print() 函數
  • C.__repr__(self)        運行時的字符串輸出;內建 repr() 函數及 ' ' 操做符
  • C.__call__(self,*args)        用於可調用的實例;能夠用來替代閉包的實現
  • C.__nonezero__(self)        爲實例定義 False 值;內建 bool() 函數
  • C.__len__(self)        長度;內建 len()

類的值比較:閉包

  • C.__cmp__(self,obj)        對象比較;內建 cmp()
  • C.__lt__(self,obj) & C.__le__(self,obj)        小於或小於等於;內建< & <=
  • C.__gt__(self,obj) & C.__ge__(self,obj)        大於或大於等於;內建 > & >=
  • C.__eq__(self,obj) & C.__ne__(self,obj)        等於或不等於;內建 = & !=

類的屬性:app

  • C.__getattr__(self,attr)        獲取屬性;內建 getattr();僅在屬性沒有找到時調用
  • C.__setattr__(self,attr)        設置屬性
  • C.__delattr__(self,attr)        刪除屬性
  • C.__getattribute__(self,attr)        獲取屬性;內建 getattr();老是被調用
  • C.__get__(self,attr)        (描述符)獲取屬性
  • C.__set__(self,attr)        (描述符)設置屬性
  • C.__delete__(self,attr)        (描述符)刪除屬性

數值類型,二進制操做符:函數

  • C.__*add__(self,obj)        加;+ 操做符
  • C.__*sub__(self,obj)        減;+ 操做符
  • C.__*mul__(self,obj)        乘;* 操做符
  • C.__*dev__(self,obj)        除;/ 操做符
  • C.__*truediv__(self,obj)        真正的除法;/ 操做符
  • C.__*floordiv__(self,obj)        地板除;// 操做符
  • C.__*mod__(self,obj)        取模;% 操做符
  • C.__*divmod__(self,obj)        除和取模;內建 divmod()
  • C.__*pow__(self,obj[,mod])        乘冪;內建 pow() ; ** 操做符
  • C.__*lshift__(self,obj)        左移位;<< 操做符
  • C.__*rshift__(self,obj)        右移位;>> 操做符
  • C.__*and__(self,obj)        按位與;& 操做符
  • C.__*or__(self,obj)        按位或;| 操做符
  • C.__*xor__(self,obj)        按位異或;^ 操做符

數值類型,一元操做符:性能

  • C.__neg__(self)        一元負
  • C.__pos__(self)        一元正
  • C.__abs__(self)        絕對值;內建 abs()
  • C.__invert__(self)        按位求反;內建 ~ 操做符

數值類型,數值轉換:code

  • C.__complex__(self, com)        內建 complex()
  • C.__int__(self)        內建 int()
  • C.__float__(self)        內建 float()

數值類型,數值壓縮:orm

  • C.__index__(self)        在有必要時,壓縮可選的數值類型爲整型(好比用於切片索引時等)

序列類型:對象

  • C.__len__(self)        序列中的項目數
  • C.__getitem__(self, ind)        獲取一個元素
  • C.__setitem__(self, ind,val)        設置一個元素
  • C.__delitem__(self, ind)        刪除一個元素
  • C.__getslice__(self, ind1,ind2)        獲取切片元素
  • C.__setslice__(self, i1, i2,val)        設置切片元素
  • C.__delslice__(self, ind1,ind2)        刪除切片元素
  • C.__contains__(self, val)        含有成員;內建 in 關鍵字
  • C.__*add__(self,obj)        串聯;+ 操做符
  • C.__*mul__(self,obj)        重複;* 操做符
  • C.__iter__(self)        生成迭代器;內建 iter() 函數

映射類型:索引

  • C.__len__(self)        類中的項目數
  • C.__hash__(self)        散列(hash)函數值
  • C.__getitem__(self,key)        獲取某個值
  • C.__setitem__(self,key,val)        設置某個值
  • C.__delitem__(self,key)        刪除某個值
  • C.__missing__(self,key)        給定鍵若不存在,則返回一個默認值

通常經常使用的特殊方法就是上面這些,某些如 coerce() 這樣在 Python3 中被刪除或失效的內建函數就沒有再列出來。由於 Python 的內建類型已經可以知足平常需求,因此下面的例子就只來實現一個功能吧:雖然 float 類型有 .hex() 方法,但內建的 hex() 函數卻不支持 float 類型。因此咱們來自定義一個能夠被內建的 hex() 調用的浮點類型。爲了省事,咱們就直接從 float 派生了~

class iFloat(float):
    def __index__(self):
        return int(self)

運行結果以下:

>>> a = iFloat(1.1)
>>> hex(a)
'0x1'

P.S. 上面這個例子是個僞栗子。由於他實際是經過將浮點數強制轉換爲整數來知足調用 hex() 函數的條件的。按照官方文檔的說法,hex() 函數只接受 int 類型作參數,你在 iFloat 裏實現 __hex__() 也沒用,這也是我把 __oct__() 和 __hex__() 從上面刪除的緣由。同時按照官方文檔的說法,若是你硬要調用內建 hex() 函數,則必須實現 __index__() 方法來返回一個整數(是的,仍是得要整數…)

即,下面這種方法是木有用的:

class iFloat(float):
    def __hex__(self):
        return self.hex()

仍然會報錯,儘管 __hex__(self) 能夠返回正常值:

>>> a = iFloat(1.1)
>>> hex(a)
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    hex(a)
TypeError: 'iFloat' object cannot be interpreted as an integer
>>> a.__hex__()
'0x1.199999999999ap+0'

我以爲這多是由於內建 hex() 函數已經不僅是調用 __hex__() 這麼簡單了。

__getattr__()與受權:

在Python 2.2 之前,標準類型還不能夠派生。爲解決此問題而常被使用的手段是「包裝」,這種方式的行爲就和他的名字同樣:

class WrappedList():
    def __init__(self,obj):
        self.__data = obj

但這還不夠,咱們要實現的主要目標是在須要的時候能夠像子類同樣自動訪問被包裝對象的屬性或方法。而實現這一功能的關鍵即是 __getattr__() 特殊方法。這個方法的做用是:當試圖訪問一個實例的屬性時,本地屬性會首先被找到,若是本地沒有就會去類屬性和祖先類屬性裏找,若是那裏也沒有的話,就會調用實例的 __getattr__() 方法。所以這裏就給咱們提供了一個將屬性訪問重定向到被包裝的類型(僞父類)的機會。其具體的實現方法即是,在 __getattr__() 中調用內建的 getattr() 函數:

class WrappedList():
    def __init__(self,obj):
        self.__data = obj
    def __getattr__(self,attr):
        return getattr(self.__data,attr)
    def __str__(self): 
        self.__atime = time()
        return str(self.__data)
    __repr__ = __str__

運行結果:

>>> a = WrappedList([1,2,3])
>>> a
[1, 2, 3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]

不過這裏仍有一個缺陷即是,當你對包裝對象進行特殊行爲時,例如上面 WrappedList 的切片操做,就會遇到問題。由於這已經超出了訪問屬性的範疇。所以在標準類型已經能夠派生的如今,就不必再去包裝他們了,至於其餘用途麼,也能夠先嚐試用裝飾器去實現。

相關文章
相關標籤/搜索