當然,如果不支援 python繼承 ,語言特性就不值得稱為“類”。派生類定義的文法如下所示:
<statement-1>
.
.
.
<statement-N>
名稱 BaseClassName 必須定義于包含派生類定義的作用域中。 也允許用其他任意表達式代替基類名稱所在的位置。 這有時也可能會用得上,例如,當基類定義在另一個子產品中的時候:
class DerivedClassName(modname.BaseClassName):
派生類定義的執行過程與基類相同。 當構造類對象時,基類會被記住。 此資訊将被用來解析屬性引用:如果請求的屬性在類中找不到,搜尋将轉往基類中進行查找。 如果基類本身也派生自其他某個類,則此規則将被遞歸地應用。
派生類的執行個體化沒有任何特殊之處: DerivedClassName() 會建立該類的一個新執行個體。 方法引用将按以下方式解析:搜尋相應的類屬性,如有必要将按基類繼承鍊逐漸向下查找,如果産生了一個函數對象則方法引用就生效。
派生類可能會重載其基類的方法。 因為方法在調用同一對象的其他方法時沒有特殊權限,調用同一基類中定義的另一方法的基類方法最終可能會調用覆寫它的派生類的方法。 (對 C++ 程式員的提示:
Python中所有的方法實際上都是 virtual 方法。)
在派生類中的重載方法實際上可能想要擴充而非簡單地替換同名的基類方法。 有一種方式可以簡單地直接調用基類方法:即調用 BaseClassName.methodname(self, arguments)。 有時這對用戶端來說也是有用的。 (請注意僅當此基類可在全局作用域中以 BaseClassName 的名稱被通路時方可使用此方式。)
Python有兩個内置函數可被用于繼承機制:
使用 isinstance() 來檢查一個執行個體的類型: isinstance(obj, int) 僅會在 obj.__class__ 為 int 或某個派生自 int 的類時為 True。
使用 issubclass() 來檢查類的繼承關系: issubclass(bool, int) 為 True,因為 bool 是 int 的子類。 但是,issubclass(float, int) 為 False,因為 float 不是 int 的子類。
多重繼承
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
對于多數應用來說,在最簡單的情況下,你可以認為搜尋從父類所繼承屬性的操作是深度優先、從左至右的,當層次結構中存在重疊時不會在同一個類中搜尋兩次。 是以,如果某一屬性在 DerivedClassName 中未找到,則會到 Base1 中搜尋它,然後(遞歸地)到 Base1 的基類中搜尋,如果在那裡未找到,再到 Base2 中搜尋,依此類推。
真實情況比這個更複雜一些;方法解析順序會動态改變以支援對 super() 的協同調用。 這種方式在某些其他多重繼承型語言中被稱為後續方法調用,它比單繼承型語言中的 super 調用更強大。
動态改變順序是有必要的,因為所有多重繼承的情況都會顯示出一個或更多的菱形關聯(即至少有一個父類可通過多條路徑被最底層類所通路)。 例如,所有類都是繼承自 object,是以任何多重繼承的情況都提供了一條以上的路徑可以通向 object。 為了確定基類不會被通路一次以上,動态算法會用一種特殊方式将搜尋順序線性化, 保留每個類所指定的從左至右的順序,隻調用每個父類一次,并且保持單調(即一個類可以被子類化而不影響其父類的優先順序)。 總而言之,這些特性使得設計具有多重繼承的可靠且可擴充的類成為可能。
私有變量
那種僅限從一個對象内部通路的“私有”執行個體變量在 Python 中并不存在。 但是,大多數 Python 代碼都遵循這樣一個約定:帶有一個下劃線的名稱 (例如 _spam) 應該被當作是 API 的非僅供部分 (無論它是函數、方法或是資料成員)。 這應當被視為一個實作細節,可能不經通知即加以改變。
由于存在對于類私有成員的有效使用場景(例如避免名稱與子類所定義的名稱相沖突),是以存在對此種機制的有限支援,稱為 名稱改寫。 任何形式為 __spam 的辨別符(至少帶有兩個字首下劃線,至多一個字尾下劃線)的文本将被替換為 _classname__spam,其中 classname 為去除了字首下劃線的目前類名稱。 這種改寫不考慮辨別符的句法位置,隻要它出現在類定義内部就會進行。
名稱改寫有助于讓子類重載方法而不破壞類内方法調用。例如:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
上面的示例即使在 MappingSubclass 引入了一個 __update 辨別符的情況下也不會出錯,因為它會在 Mapping 類中被替換為 _Mapping__update 而在 MappingSubclass 類中被替換為 _MappingSubclass__update。
請注意,改寫規則的設計主要是為了避免意外沖突;通路或修改被視為私有的變量仍然是可能的。這在特殊情況下甚至會很有用,例如在調試器中。
請注意傳遞給 exec() 或 eval() 的代碼不會将發起調用類的類名視作目前類;這類似于 global 語句的效果,是以這種效果僅限于同時經過位元組碼編譯的代碼。 同樣的限制也适用于 getattr(), setattr() 和 delattr(),以及對于 dict 的直接引用。
雜項說明
有時會需要使用類似于 Pascal 的“record”或 C 的“struct”這樣的資料類型,将一些命名資料項捆綁在一起。 這種情況适合定義一個空類:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
一段需要特定抽象資料類型的 Python 代碼往往可以被傳入一個模拟了該資料類型的方法的類作為替代。 例如,如果你有一個基于檔案對象來格式化某些資料的函數,你可以定義一個帶有 read() 和 readline() 方法從字元串緩存擷取資料的類,并将其作為參數傳入。
執行個體方法對象也具有屬性: m.__self__ 就是帶有 m() 方法的執行個體對象,而 m.__func__ 則是該方法所對應的函數對象。