【Python學習】python學習手冊--第三十一章 類的高級主題

新式類

  • 在Python3.0中,所有類都是新式類(new-style),所有的類都繼承自object對象,不管是顯式的還是隱式的,並且所有對象都是object的實例。
  • 在Python2.6和以前的版本,類必須顯式繼承object纔會是新式類,並且獲得所有新式類的特性

在Python3.0中,所有的類都自動繼承自新式類,所以新式類的特徵只是常規的特性。而在Python2.6版本及以前的都必須繼承自object類或者繼承自Python內置類纔會成爲新式類。

實際上,新式類在語法和行爲上幾乎與經典類完全向後兼容;它們主要只是添加了一些高級的新特性。

新式類的變化有以下幾點:

  • 類就是類型,類型也是類,同時一個實例的類型也是該實例的類,它們之間的區別已經完全消失,
>>> type([1,2,3])  #類型就是類
<class 'list'>
>>> type(list)     #類也是類型
<class 'type'>   
>>> list.__class__
<class 'type'>
>>> 
  • 所有的類都是派生自object類
  • 新式類在多重繼承的類樹中搜索是先從同一個深度中的類開始搜索,所有同深度的類都搜索完成之後,然後再開始搜索更深類(而2.6是優先將一個樹枝搜索完後,再開始搜索下一個樹枝,新式類是廣度優先而不是深度優先)。如果要避免這種搜索順序的混淆,最好將變量命名得更加有特效,類的設計更加合理。

新式類還有一些更高級的擴展:

slots屬性

在新式類中,__slots__是比較特殊的類屬性,可以將字符串列表賦值給該屬性,這樣就可以限制類實例的合法屬性集,那些不合法的屬性將無法賦值,這又能優化內存和速度性能。

>>> class c:
...   __slots__=['a','b','c']
... 
>>> lxm=c()
>>> lxm.d=1                    #賦值不會成功
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'c' object has no attribute 'd'
>>> lxm.a=1
>>> lxm.b=2
>>> lxm.c=3
>>> lxm.z=2222
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'c' object has no attribute 'z'
>>> 

如果類的屬性只有確定的幾個時,創建實例並分配命名空間時很費內存的辦法,__slots__屬性可以順序存儲,而不是用傳統的字典形式來存儲屬性,這樣提高了執行速度,也節省空間。
當有多個超類的時候,__slots__屬性可以看做是所有超類該屬性的集合。與超裂相關slots屬性有以下幾點需要注意:

  • 如果一個子類繼承自一個沒有__slots__的超類,那麼超類的dict屬性總是可以訪問,這讓子類中的slots屬性失去意義。
  • 如果一個子類和超類相同的slots名稱,超類的slots屬性只有通過超類標識符獲取其屬性
  • 由於一個slots聲明的含義受到類的限制,所以子類將有一個__dict__,除非它們也定義了一個slots

property機制

這個property是一個對象,賦值給類屬性名稱。它會產生三種方法(get,set,del)如果任何參數是None,那麼該運算就不能支持。特性賦值一般都是在類的頂層處使用。

>>> class c():
...     def Cget(self):
...         print("Cget called,value:")
...         return 999
...     def Cset(self,value):
...         print("Cset called,value:",value)
...         return 888
...     def Cdel(self):
...         print("Cdel called")
...         del self
...     test=property(Cget,Cset,Cdel,None)
... 
>>> lxm=c()
>>> lxm.test
Cget called,value:
999
>>> lxm.test=123
Cset called,value: 123

這種效果就很像重載運算符中的__getattr__和__setattr__方法。

靜態方法

有些時候我們需要對類的屬性進行操作,而不是類的實例。我們就需要定義一個不需要傳入實例(self)的“靜態”方法。在Python2.6中,無論是否在定義方法是寫沒寫self參數,在調用方法是都會需要一個實例。而在Python3.0中就不一樣,如果不需要實例,是完全可以不傳入實例對象,調用時需要通過類名調用,如果使用實例調用,會默認傳入實例對象,導致接受參數出錯。
使用靜態方法最好的方法是將靜態方法寫入到類方法中,使得每一個實例都可以調用,調用的結果就是可以改變類屬性。

內置函數staticmethod和classmethod可以把類中的方法標記成爲特殊的方法(標記成爲靜態方法和類方法。)標記之後,就會將Python默認傳遞第一個參數的機制給屏蔽掉

>>> class c():
...     def func():
...         print("func called")
...     func2=staticmethod(func)
... 
>>> lxm=c()
>>> lxm.func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes 0 positional arguments but 1 was given    #默認情況下還是傳入了實例
>>> lxm.func2()        #通過內置staticmethod就不會傳入實例對象了
func called
>>> 

類似的,用classmethod函數時,就將方法標記爲類方法,默認情況下,就將該實例的類傳入成爲第一個參數。

從技術上講,Python現在支持三種類相關方法:實例,靜態和類。

  • 實例方法就是必須用實例來調用的方法,Python會把實例對象當作方法的第一個參數傳入方法內。
  • 靜態方法調用時不需要實例參數,其變量屬於類,是在類的範圍內,屬於局部變量。
  • 類方法與實例方法類似,但是第一個參數傳入的應該是類,而不是實例。

裝飾器和元類

staticmethod和classmethod這類函數屬於Python中的函數裝飾器,它們就像是在函數外包裹了一層,替函數明確了特定的運算模式,返回了一個新的函數對象。函數裝飾器類似於委託設計模式,但是其設計是爲了增強特定的函數或方法調用,而不是整個對象接口。
staticmethod裝飾器編寫還可以使用以下語法:
這兩種語法都是等價的:

class c:
   @staticmethod
   def meth():
      <statements>

class c:
  def meth():
      <statements>
  meth=staticmethod(meth)

staticmethod仍然是一個內置函數,它可以用於裝飾語法中,是因爲它把一個函數當作參數並返回一個可調用的對象。
這些都是Python的高級應用,對工具編寫者有意義,但對於應用程序員就沒那麼重要。
適當的使用類,可以提高代碼的複用性,但是過多的包裝和繼承絕對不是好事,這讓代碼的邏輯太深,維護性和可讀性太差。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章