Python3.4 Tutorial9 - Classes (Part 3)
9.5 繼承(Inheritance)
當然,一個有“類”的語言中如果不支援繼承的特性,是沒有什麼價值的。派生類(Derived Class)的定義語法如下:
class DerivedClassName(BaseClassName): <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的子類。
9.5.1 多繼承(Multiple Inheritance)
Python是支援多繼承的。一個類帶有多個基類的定義方式如下:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
在大多數情況下,你可以簡單的認為在父類中查詢繼承的屬性是一個深度優先、從左到右的過程,同一個類裡在繼承結構中重疊的部分不會被搜尋兩次。因此,如果一個屬性在DerivedClassName中找不到,它會在Base1中查詢,然後(遞迴的)在Base1的基類中查詢,如果都沒有找到,會去Base2中查詢,以此類推。
實際的查詢過程會稍微複雜一些;方法的搜尋順序會根據super()的呼叫而動態變化(附上原文:method resolution order changes dynamically to support cooperative calls to super())。這種方法在其他的多繼承語言中以call-next-method被熟知,並且比單繼承語言中的超類呼叫更有用。
動態排序是很有必要的,因為所有的多繼承關係都是呈一個或多個菱形結構(在從最底層的類到父類的多條路徑中至少有一條是可以訪問到的)。例如,所有的類都繼承自object,所以任何一個多繼承都提供了不止一條到達object的路徑。為了保證基類可以被多次訪問到,動態演算法將查詢順序線性化,使每個類中都是保持從左到右的順序(翻譯的比較混亂,原文是:the dynamic algorithm linearizes the search order in a way that preserves the left-to-right ordering specified in each class),這樣每個父類只被呼叫一次,就不再改變了(這意味著,一個類被繼承時不會影響它父類的搜尋順序。)。綜上考慮,這個屬性使得使用多繼承設計一個可靠的、可擴充的類成為可能。更多的細節,請參考https://www.python.org/download/releases/2.3/mro/。
9.6 私有變數(Private Variables)
在Python中,不存在無法訪問的“私有”例項變數。然而,在大多數的Python程式碼中都會遵循這樣的約定:以下劃線'_'為字首的名稱(例如_spam)應當作為非公有的API來對待(無論它是函式、方法還是一個資料成員)。應該把它作為一個實現細節對待,並且如果改變了也不會有任何通知。
因為有一箇中合法的使用類私有成員的情況(即為了避免和子類中的名稱衝突),所以為這個機制提供了有限的支援,稱為名稱重整(name mangling)。任何使用__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 # 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)
請銘記,重整的規則的設計是用來避免衝突的;所以,仍然可以訪問或修改一個被認為是私有的變數。在特殊情況下這也可能是有用的,例如除錯的時候。
注意,傳遞給exec()
或eval()
的程式碼不會認為呼叫類的類名是當前類;這和global
語句的作用類似,位元組編譯在一起的程式碼也有這個限制。相同的限制也適用於getattr()
,setattr()
和delattr()
,以及直接引用__dict__
的時候。
9.7 雜記(Odds and Ends)
有時候,有和Pascal中的“記錄(record)”或C中的“結構體(struct)”相似的資料型別會很有用,可以將一些命名後的資料繫結在一起。一個空的類定義可以很好的做到這個:
class Employee: 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__
是代表方法的那個函式物件。
9.8 異常也是類
使用者定義的異常也是被類標識的。應用這個機制,可以建立出可擴充的異常繼承體系。
raise
語句有兩個新的(語義上)合法的形式:
raise Class raise Instance
第一種形式,Class
必須是type的例項或從它的一個派生類。第一種形式是下面的簡寫:
raise Class()
在except子句中的類和被丟擲的異常類自身或它的基類是相容的(但是反過來卻不行——except子句中列出的派生類,和它們基類的異常是不相容的)。例如,下面的程式碼會按順序輸出B,C,D:
class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")
注意,如果這些異常子句的順序倒過來(except B
放在第一個位置),它會列印B,B,B——第一個except子句總會匹配上。
當一個未處理的異常的錯誤訊息被列印出來時,異常類的類名會被列印,後面跟著一個冒號和一個空格,最後是使用內建函式str()轉換為string的異常例項。
相關文章
- Python3.4 Tutorial9 - Classes (Part 4)Python
- Python3.4 Tutorial9 - Classes (Part 2)Python
- Python3.4 Tutorial9 - Classes (Part 1)Python
- 3 About Kernels and Traits ClassesAI
- Python3.4 Tutorial11 - Brief Tour of the Standard Library – Part IIPython
- [翻譯]The Neophyte's Guide to Scala Part 12: Type ClassesGUIIDE
- Python高階特性(3): Classes和MetaclassesPython
- Type classes in Scala
- 核心編譯part3編譯
- Study Plan For Algorithms - Part3Go
- Datatable Scroller (Server Side) Part:3ServerIDE
- 疫情視覺化part3視覺化
- python3.4之決策樹Python
- IDAPython 讓你的生活更滋潤 – Part 3 and Part 4Python
- Linux_java_呼叫classesLinuxJava
- DBReader/Classes/LogonGo
- [part 3] 第一個 Django 應用Django
- 深入淺出Rust Future Part-3Rust
- Canvas基礎-粒子動畫Part3Canvas動畫
- django入門-檢視-part3Django
- Android文字時鐘 — Part3Android
- 8086 彙編學習 Part 3
- python3.4支援xp麼Python
- [MetalKit]4-Using-MetalKit-part-3使用MetalKit3
- PostgreSQL DBA(46) - PG Operator classes and familiesSQL
- [ES6深度解析]12:Classes
- Python “黑魔法” 之 Meta ClassesPython
- [SwiftUI 100天] Bucket List - part3SwiftUI
- XtraBackup工具詳解 Part 3 XtraBackup工作原理
- CKKS Part3: CKKS的加密和解密加密解密
- .Net 中的反射(反射特性) - Part.3反射
- 系列化入門讀物-PART 3 (轉)
- python3.4連線oracle資料庫PythonOracle資料庫
- 【Arranging an Appointment】Unit1 Part3APP
- Part 3: OAuth2進行身份驗證OAuth
- Go for PHP Developers: Structs vs Classes (翻譯)GoPHPDeveloperStruct
- Function 與 Classes 元件的區別在哪?Function元件
- lecture8 Template Classes + Const Correctness