關於類的基礎內容,包括類的定義,類對象,實例對象,方法對象,類和實例變量。在下面的這篇文章已經有基本的介紹:python
https://blog.csdn.net/weixin_45642918/article/details/104579523微信
今天這篇文章主要介紹類的繼承以及私有變量。app
Python 支持類的繼承。下面是派生類的定義:函數
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>
在這裏基類 BaseClassName 與派生類必須定義在同一個做用域中。也容許其餘任意表達式代替基類名稱所在的位置。例如,基類定義在另外一個模塊中時:spa
class DerivedClassName(modname.BaseClassName):
派生類跟基類執行過程是同樣的。當構造類對象的時候,基類實際上是會被先記下。當解析屬性引用的時候:若是請求屬性在派生類中沒有找到時,將會從基類中進行搜索。如果繼承的基類也是派生自其餘類,則遞歸應用此規則。.net
派生類的實例化也沒有太特殊的地方:DerivedClassName() 建立類的新實例。方法引用解析以下:搜索相應的類屬性,若是有必要的話,會按基類繼承鏈逐步往下找,若是產生了一個函數對象則方法引用就生效。例如:設計
>>> class Animal: ... def run(self): ... print('Animal is running') ... >>> class Dog(Animal): ... pass ... >>> d = Dog() >>> d.run() Animal is running
在這裏,Dog 繼承 Animal 基類,Dog()
建立新的實例並賦值給 d
,當調用 run
方法的時候,Dog 類並無相應的方法,因此在基類 Animal 中搜索,Animal 中存在這個方法,產生函數對象,引用生效。返回的結果則是基類方法中的內容。code
派生類是能夠重載基類的方法。由於當調用同個對象的其餘方法時,基類方法是沒有其餘特殊權限的。因此基類的方法調用同一基類中定義的另外一個方法可能最終會調用覆蓋它的派生類的方法。對象
在上面的例子中,Dog 並無相應的方法,如今也編寫一個一樣名爲 run 的方法,再看調用結果:blog
>>> class Animal: ... def run(self): ... print('Animal is running') ... >>> class Dog(Animal): ... def run(self): ... print('Dog is running') ... >>> d = Dog() >>> d.run() Dog is running
在這裏,Dog 類的 run()
方法重載基類 Animal 的 run()
方法,當調用 run()
方法的時候,會首先調用子類重載的方法。
但在派生類中的重載方法實際上可能想要擴展而並不是簡單地替換同名的基類方法。可是有一種方式能夠簡單地調用基類方法:BaseClassName.methodname(self, arguments)
。(這裏須要注意,這種方式僅在基類 BaseClassName
可在全局做用域以此名稱被訪問時生效。)例如:
>>> class Animal: ... def run(self): ... print('Animal is running') ... >>> class Dog(Animal): ... def run(self): ... print('Dog is running') ... >>> d = Dog() >>> d.run() Dog is running >>> Animal.run(d) Animal is running
Animal.run(d)
直接調用基類的方法。
Python 也支持多重繼承方式。派生類具備多個基類的定義方式以下:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
在最簡單的狀況下,搜索從基類繼承屬性的操做是深度優先,從左到右,當存在層次結構重疊的狀況下,是不會在同一個類中搜索兩次的。具體可能的狀況:當某一個屬性在 DerivedClassName 中沒有找到,則會在基類 Base1 中先查找,而後遞歸到 Base1 的基類中去查找,如果未找到,則搜索 Base2,依此類推。
但真實的狀況可能會複雜一些:方法解析順序會動態改變以支持對 super()
的協同調用,具體的細節能夠參考下面的資料
https://www.python.org/download/releases/2.3/mro/
其實嚴格意義上的僅限從一個對象內部訪問的「私有」實例變量在 Python 中是並不存在的。可是,大多數 Python 代碼都會遵循這樣的一個約定:帶有一個下劃線的名稱(例如 _spam
) 應該被看成是 API 的非公有部分(不管它是函數、方法仍是數據成員)。
在避免名稱與子類所定義的名稱衝突,Python 存在這對此種機制的支持,被稱爲名稱改寫。形如 __spam
這樣的標識符(至少帶有兩個前綴下劃線,至多一個後綴下劃線)將被替換爲 _classname__spam
,其中 classname
爲去除前綴下劃線的當前類名稱。
這種名稱改寫的支持可以有助於子類重載方法而不破壞類內方法調用。例如:
class Mapping: 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 = update class MappingSubclass(Mapping): def update(self, keys, values): # 爲 update() 提供新的特徵 # 可是不破壞 __init__() for item in zip(keys, values): self.items_list.append(item)
在上面的例子當中,即使 MappingSubclass
引入一個 __update
標識符的狀況下,也不會出錯,由於在 Mapping
中它已經被替換爲 _Mapping__update
,在 MappingSubclass
則被替換爲 _MappingSubclass__update
。
在這裏須要注意:改寫規則的設計主要是爲了不衝突。這也使得修改或訪問這些被視爲私有的變量成爲可能。在某些特殊的狀況下,可能頗有用。但同時也須要注意,若非必要,不要直接訪問或修改這些被視爲私有的變量。
以上就是關於類(Class)繼承和私有變量的內容。
歡迎關注微信公衆號《書所集錄》