使用者:Xyy23330121/Python/類的私有量與類的繼承
為篇幅起見,另起一個頁面講這些內容。
類的一個重要方法是繼承。我們可以這樣定義一個類型的子類型:
class C: pass
class D(C): pass
此時, D
會繼承來自 C
的一切屬性或方法。這種單重繼承的方式和定義函數時、變量的作用域很相似的。我們可以簡單理解為:D
是 C
的子類型時,D
的作用域也是 C
作用域的下級作用域。
class C: pass
class D: pass
class E(C,D): pass
通過以上方法,可以讓 E
同時作為 C
和 D
的子類型。
子類繼承父類型方法的順序是按照其mro()
方法所示的順序的,讀者可以通過輸出 ClassName.mro()
的結果,得知 Python 會按照什麼順序去尋找方法或屬性。
以上述例子為例,E.mro()
的輸出為:
<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>
即,它會先從類型 E 中查找方法。找不到時,再查找類型 C。若還找不到,則查找類型 D。最後查找類型 object。
如果這個順序無法生成,則新的子類無法正常創建,並且會報錯。比如:
class RootA: pass
class RootB: pass
class C(RootA, RootB): pass
class D(RootB, RootA): pass
class E(C,D): pass #报错:TypeError: Cannot create a consistent method resolution order (MRO) for bases RootA, RootB
讀者在完全了解這些順序之前,應當不要隨意使用多重繼承。具體順序,參見此文檔。
大多數 Python 代碼遵循一個規律:以下劃線開頭的名稱應該被當作是私有部分,不應從外面隨意更改——如果進行更改,可能會破壞一部分功能。這個思想在「類」上的體現是一個「名稱改寫」的功能。
class C:
__a = 0
def prt(self):
print(self.__a)
#print(C.__a) #报错:AttributeError: type object 'C' has no attribute '__a'
print(C._C__a)#输出:0
C.prt(C) #输出:0
具體來講,在類型 ClassName
中使用以兩個下劃線開頭的函數或屬性時,Python 會自動把屬性名中的 __
改為 _ClassName__
。而在類型外時,則不會進行這樣的改寫。
在 Python 中,任何變量的數值都是可以改寫的。
class property(fget=None, fset=None, fdel=None, doc=None)
property 是一個特殊的、用作類型屬性的類型。對 property 實例進行的讀取、賦值和更改等操作,都會被改為執行 fget、fset 和 fdel 函數。比如以下示例:
class C:
def __init__(self): self.__x = 0
def getx(self): return self.__x
def setx(self, value): self.__x = value
def delx(self): del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")
c = C()
print(c.x) #输出:0
c.x = 1
print(c._C__x) #输出:1
del c.x
try: print(c._C__x)
except: print("c._C__x deleted")
#输出:c._C__x deleted
property 支持作為 裝飾器 來使用。以下代碼得到的結果和上面是相同的:
class C:
def __init__(self): self.__x = 0
@property
def x(self): return self.__x
@x.setter
def x(self, value): self.__x = value
@x.deleter
def x(self): del self.__x
我們可以利用 property,來達成類似「只讀屬性」的效果,比如:
class AlwaysZero:
@property
def x(self): return 0
@x.setter
def x(self, value): raise AttributeError("readonly attribute")
@x.deleter
def x(self): raise AttributeError("readonly attribute")
a = AlwaysZero
print(a.x) #输出:0
a.x = 1 #报错:AttributeError: readonly attribute
這樣,就實現了只讀屬性的效果。以上代碼還可以簡化為:
class AlwaysZero:
@property
def x(self): return 0
a = AlwaysZero()
print(a.x) #输出:0
a.x = 1 #报错:AttributeError: property 'x' of 'AlwaysZero' object has no setter
classinfo
應輸入類型、多個類型組成的元組或聯合類型。
如果 object
是 classinfo
中某個類型的實例,或某個類型的、子類型的實例。則返回 True
,反之,返回 False
。
同 isinstance
,classinfo
應輸入類型、多個類型組成的元組或聯合類型。
如果 class
是 classinfo
中某個類型的子類型,則返回 True
,反之,返回 False
。
抽象基類是用於判斷某個自定義類是否包含對應方法的。比如 collections.abc
模塊中的 collection.abc.Sized
。當一個類,其實例具有 __len__
方法時,該類就會被視為是 collection.abc.Sized
的一個子類。這和創建該類時,是否顯式地說明該類繼承於 collection.abc.Sized
無關。
這個子類關係,是可以通過上述 isinstance
和 issubclass
函數檢測出來的。這在一些情況下很有用。