Python的類提供了面向對象編程的所有基本功能:
- Object類是所有類的父類(不需要明確指定);
- 類允許繼承多個基類(使用逗號分割);
- 派生類可覆蓋基類中任何方法;
類基礎
類支持兩種操作:
- 實例化:
inst = clsName(…)
; - 屬性引用: 使用實例對象或類(類屬性)引用對象;
類定義
類通過class定義,裏面有屬性與方法。
class ClassName:
<statements>
def funs(self, arg):
# self is the instance of class
<fun statements>
@classmethod
def clsFuns(cls, arg):
# cls is the class
<statements>
@staticmethod
def staticFun(arg):
# 與普通函數類似
<statements>
類繼承
Python支持多繼承,多個父類之間通過逗號分割。若是父類中有相同的方法名,而在子類使用時未指定,Python從左到右依次查找父類中是否包含方法(即優先使用排在前面的父類中的方法)。
在多重繼承(特別是有菱形繼承時),查找關係會更復雜;具體是通過MRO(Method Resolution Order)
來解決方法調用二義性問題的;而MRO又是使用的C3算法(拓撲排序+深度優先搜索:依次取出入度爲0的節點,取出節點後即把其相關的邊去掉,然後遞歸處理)處理順序的。
子類中要調用基類中同名方法需:baseCls.fun
或super().fun
;
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
<statement-N>
變量與可訪問性
變量(屬性)分類:
- 類變量:直接定義在類中,爲所有類對象共享;通過類名訪問
clsName.var
; - 實例變量:每個實例獨有的數據(在
__init__
方法中定義、初始化);通過實例對象訪問inst.var
;
Python中的可訪問性是通過約定來實現的:
- 私有屬性:以兩個下劃線開始的,
__var
; - 保護屬性:以一個下劃線開始的,
_var
;只能自身與子類可訪問; - 普通屬性:以字母等開始的。
類專有方法
Python中通過約定一些專有的方法來增強類的功能:
__init__
:構造函數,在生成對象時調用(實例變量也在此函數中定義);__del__
:析構函數,釋放對象時使用;__repr__
:打印(若有__str__
,則先嚐試str),轉換;__setitem__
:按照索引賦值;__getitem__
:按照索引取值;__len__
:獲取長度,內置函數len()
使用;__cmp__
:比較運算;__call__
:函數調用(對象看作一個算子);__add__
:加運算;__sub__
:減運算;__mul__
:乘運算;__div__
:除運算;__mod__
:求餘運算;__pow__
:乘方運算;
repr與str:repr()與str()爲內置函數,對應類中的__repr__
與__str__
來處理字符串:
- repr對Python(程序員)友好,生成的字符串應可通過eval()重構對象;
- str爲用戶友好,方便用戶理解的輸出;
- print時先查看
__str__
,若未定義,再查看__repr__
;
類進階
類方法
Python類中有三種方式定義方法:
- 常規方式:定義實例方法,第一個參數表示實例(一般約定爲self);
@classmethod
修飾方式:定義類方法,第一個參數表示類(一般約定爲cls);@staticmethod
修飾方式:定義靜態方法;
以下是三種函數的定義及調用方式:
class MethodTest:
index = 0
def __init__(self):
self.index = -1
def instFun(self, index):
print('instance function:', index, self.index, MethodTest.index)
self.index = index
@classmethod
def classFun(cls, index):
print('class function:', index, cls.index)
cls.index = index
@staticmethod
def staticFun(index):
print('static function:', index, MethodTest.index)
if __name__=="__main__":
mtest = MethodTest()
mtest.instFun(1) # instance function: -1 0
mtest.classFun(2) # class function: 0 0
mtest.staticFun(3) # static function: 3 2
MethodTest.classFun(4) # class function: 2 2
MethodTest.instFun(mtest, 5) # instance function: 1 4
MethodTest.staticFun(6) # static function: 6 4
通過實例對象可以直接調用三類方法;通過類名可以直接調用靜態方法與類方法,若要調用實例方法,還需傳遞一個實例對象。
類屬性
通過get/set
屬性,可以增強對變量的控制,便於後續修改與擴展。但類中的屬性(get/set
)方法不要手動去實現,應使用@propery
來修飾實現:
- 把一個get方法變成屬性,只需增加
@propery
修飾即可; @propery
本身又創建了另一個裝飾器,負責把一個setter方法變成屬性賦值:若不增加setter方法,則屬性將是隻讀的。
以下是屬性定義的示例:通過@propery
定義了count的get屬性,然後就可以通過@count.setter
來定義set屬性了。
class CountProp:
def __init__(self):
self._count=0
@property
def count(self):
return self._count
@count.setter
def count(self, newCount):
self._count = newCount
def __getattr__(self, name):
print('call __getattr__:', name)
value = '{} value for {}'.format(self._index, name)
self._index += 1
setattr(self, name, value)
return value
def __getattribute__(self, name):
print('call __getattribute__:', name)
try:
return super().__getattribute__(name)
except AttributeError:
# setattr(self, name, value)
raise
cPro = CountProp()
print(cPro.count) # 0
cPro.count = 10
print(cPro.count) # 10
類中的屬性除在類定義(或初始化時)定義外,還可以在任何時候綁定(在給不存在的屬性賦值時會自動綁定)。屬性的獲取與賦值規則爲:
- 先調用
getattribute
:無論屬性是否存在(若屬性不存在,需拋出AttributeError異常),因此不能在此方法內引用屬性,否則會引起循環遞歸。因此若要訪問屬性,則調用super().XXX來避免遞歸。 - 屬性不存在時(對象的實例字典中查詢不到屬性時),若類定義了
getattr
則調用此方法; - 當給屬性賦值或調用setattr函數時,都會觸發
setattr
方法。