當然,如果不支持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__ 則是該方法所對應的函數對象。