新式類
- 在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的高級應用,對工具編寫者有意義,但對於應用程序員就沒那麼重要。
適當的使用類,可以提高代碼的複用性,但是過多的包裝和繼承絕對不是好事,這讓代碼的邏輯太深,維護性和可讀性太差。