1. 程式人生 > >python面向對象編程 -- 封裝、繼承(python3.5)

python面向對象編程 -- 封裝、繼承(python3.5)

style 單調性 dict bsp 細節 劃線 private sel 搜索

面向對象編程三要素:封裝、繼承和多態。本文主要看和封裝、繼承相關的概念;在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__查看其父類。

  1. 子類可以引用父類命名空間中的所有屬性。屬性的查找順序:實例-->子類-->父類-->父類的父類...-->object。python3中所有的類都隱性繼承自object類。
  2. 子類可以重寫父類的屬性。一旦屬性重寫,對於子類或子類之前開始的屬性查找,父類對應的屬性相當於被屏蔽掉了。
  3. 如果子類要重寫初始化方法,最好通過擴展父類的初始化方法實現,即調用父類的初始化方法,並實現自己的個性化擴展。
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)