標籤:機率 print html name 屏蔽 繼承 amp pam hello
物件導向編程三要素:封裝、繼承和多態。本文主要看和封裝、繼承相關的概念;在python中多態的概念比較模糊,本文不做討論。
1 封裝
封裝:將資料和操作組裝到一起,對外只暴露一些介面供類外部或子類訪問,隱藏資料和操作的實現細節。
在其他物件導向語言,比如java中,屬性存取控制一般有三種狀態:private、protectd、public。python中沒有什麼東西是完全不可見的,沒有任何機制可以強制性的隱藏資料。所以在python中不存在真正的只能在對象內部訪問的屬性。
一個被大多數的python程式員遵守的約定:以底線開頭的變數應該被當作非公有屬性對待,即它應該被當作是實現細節,其修改應該是不被察覺的。
1.1 名稱管理
python中有一種機制對類成員的私人化提供有限的支援,稱為名稱管理:
任何以至少兩個底線開頭、至多一個底線結尾的標識符,如__spam,都會被轉換為_classname__spam的形式,其中classname是當前的類名。這種名稱管理機制不考慮標識符實際歸屬哪個命名空間,只要它發生在類的定義中,就會自動生效。
class Chinese: """A sample example class""" nationality = ‘China‘ def __init__(self, name, age, gender): self.name = name self.__age = age self.gender = gender def __str__(self): return ‘{}(name={}, age={}, gender={})‘.format(self.__class__, self.name, self.__age, self.gender)xm = Chinese(‘xiaoming‘, 18, ‘male‘)print(xm.__dict__) # {‘gender‘: ‘male‘, ‘name‘: ‘xiaoming‘, ‘_Chinese__age‘: 18}print(xm) # <class ‘__main__.Chinese‘>(name=xiaoming, age=18, gender=male)print(xm.__age) # AttributeError: ‘Chinese‘ object has no attribute ‘__age‘
- __age在類中被自動轉化為_Chinese__age儲存在執行個體的命名空間中;
- 類內部出現的__age形式的標識符都會被自動轉換,所以__str__方法可以訪問本例中的__age屬性;
- 在類外部(包括在子類中),無法訪問__age屬性,因為執行個體命名空間中並不存在該屬性。
繼承
繼承:對現有類的一種複用機制。如果相對現有的類做一些個人化的修改,可以通過繼承實現,而不是直接修改原類。
python的繼承文法如下:
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>
BaseClassName代表被繼承的類,稱為基類或者父類。
DerivedClassName稱為子類。除了添加了父類,子類的定義和執行個體化和普通的類並沒有什麼區別。可以通過BaseClassName.__bases__查看其父類。
- 子類可以引用父類命名空間中的所有屬性。屬性的尋找順序:執行個體-->子類-->父類-->父類的父類...-->object。python3中所有的類都隱性繼承自object類。
- 子類可以重寫父類的屬性。一旦屬性重寫,對於子類或子類之前開始的屬性尋找,父類對應的屬性相當於被屏蔽掉了。
- 如果子類要重寫初始化方法,最好通過擴充父類的初始化方法實現,即調用父類的初始化方法,並實現自己的個人化擴充。
class Animal: def __init__(self, name, age): self.name = name self.age = age def reply(self): return self.speak() def speak(self): return ‘Hello world‘class Cat(Animal): def __init__(self, name, age, breed): super().__init__(name, age) self.breed = breed def speak(self): return ‘Cat Miaow‘print(Cat.__bases__) # (<class ‘__main__.Animal‘>,)garfield = Cat(‘Garfield‘, 10, ‘Garfield‘)print(garfield.reply()) # Cat Miaow
super().__init__引用Animal之前的祖先類的初始化方法。
多繼承
python是支援多繼承的,其文法如下:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
多繼承要解決的主要問題是屬性搜尋順序:
1. 將類及其祖先類按照搜尋優先順序從高到低排列產生一個列表的過程稱為類的線性化。
2. MRO: Method Resolution Order, 指用於類線性化的規則,python3用的是C3演算法。C3演算法可以保證線性化的單調性,單調性是指:如果在類C的線性化中,C1的優先順序高於C2,那麼C的所有子類的線性化中,C1的優先順序高於C2。
3. 可以通過 DerivedClassName.__mro__ 查看類的線性化結果。
想瞭解C3演算法的具體實現,可以參考文章:https://www.python.org/download/releases/2.3/mro/
其他
資料屬性和方法屬性可能會出現標識符衝突,這會導致非預期的屬性重寫(對於標識符而言,賦值即定義),這在大型項目中會引起難以定位的bugs。為了避免標識符衝突,使用一些約定最小化衝突機率是明智的。參考方案:
- 大寫方法屬性的首字母,給資料屬性標識符添加特定的首碼
- 使用動詞標識方法屬性,使用名詞標識資料屬性
參考:
1 file:///Library/Frameworks/Python.framework/Versions/3.5/Resources/English.lproj/Documentation/tutorial/classes.html
python物件導向編程 -- 封裝、繼承(python3.5)