官术网_书友最值得收藏!

7.3 類繼承

繼承是代碼重用的好辦法,Python中的繼承就像現實生活中的繼承一樣,子類可以什么都不做就擁有了父類的屬性或方法。

7.3.1 單繼承

單繼承就是一個子類只有一個基類的繼承方式,語法是:class子類名(基類名): …。

還是以汽車為例來講解單繼承。汽車不是只有燃油的傳統小汽車,還有特斯拉這種電動車,雖然它們都是汽車,都有品牌、顏色等屬性,但是在一些細節上還是有區別的。據此可以定義一個Car基類,它包含所有汽車的通用屬性,然后再定義一個OilCar類和ECar類分別代表燃油汽車和電動車:

接下來分別實例化OilCar和ECar類,看看它們是不是真的能夠使用基類的屬性與方法:

由此可見,對象o是OilCar類型,同時也是Car的一個實例。子類不但可以直接使用父類的屬性(brand、color)與方法(print_car),而且還可以增加新方法(power)。

7.3.2 多繼承

多繼承就是一個子類可以繼承多個父類的繼承方式,相對于單繼承來說,多繼承更復雜也更難以控制,容易造成菱形繼承問題,即兩個父類同時繼承了一個基類,而子類會包含多個父類的內容,產生代碼歧義,因此很多編程語言都摒棄了這種繼承方式如Java和C#。Python是允許多繼承的,這對于開發人員來說即提供了更多的代碼編寫方案,同時也引入了更多的潛在問題,因此開發人員應時刻注意多繼承的風險。

雖然編者并不建議開發人員使用多繼承的方式編寫代碼,但是仍在這里對多繼承做一個簡單介紹,多繼承的語法與單繼承類似:class子類名(基類1,基類2…): …。下面是一個非常簡單的多繼承的例子:

類C同時繼承了類A和類B,那么它應該同時擁有類A和類B的屬性與方法,執行以下代碼進行查看:

    >>> c = C()
    >>> c.run(5)
    我在以5米/秒的速度跑步
    >>> c.speak("你好")
    我在說: 你好

上面的代碼非常簡單,因為類A和類B既沒有構造函數也沒有重復的屬性和方法,那么如果它們都有構造函數并且有同名的方法時會發生什么呢?

執行以下代碼:

    >>> c = C(18)
    >>> c.intro()
    我叫 18
    >>> c.run(5)
    我在以5米/秒的速度跑步
    >>> c.speak("你好")
    我在說: 你好

從上面的執行結果來看,本來我想介紹我的年齡,但是卻輸出了“我叫18”,而另外兩個方法還能正常執行。這種現象是繼承順序導致的,類A在類B的前面,所以對于同名的屬性與方法子類都會調用類A的。

上面是普通方法在多繼承中的表現,對于構造方法來說就更復雜了。Python中類的構造方法基本按照以下方式執行。

如果子類有自己的構造方法,那么在實例化子類的時候就會執行子類的構造方法,不會執行基類的構造方法,例如:

如果子類沒有構造方法,在單繼承中則會直接調用基類的構造方法,例如:

如果子類有多個基類并且子類沒有自己的構造函數,則會按順序查找父類,找到第一個有構造函數的基類并執行,例如:

7.3.3 方法重載

有時雖然父類已經提供了一些方法,但是這些方法可能不能滿足子類的需求,所以可以在子類中對父類方法進行重寫。

還是以單繼承為例,前面的例子中子類OilCar和ECar都直接使用了父類的print_car()方法,接下來希望在print_car()函數的輸出中還能展示當前車輛的動力類型,此時就需要重寫父類的print_car()方法,具體修改如下:

重新調用print_car()方法查看執行結果:

    >>> o = OilCar("奔馳", "紅色")
    >>> o.print_car()
    品牌: 奔馳 , 顏色: 紅色 , 動力:汽油
    >>> e = ECar("特斯拉", "黑色")
    >>> e.print_car()
    品牌: 特斯拉 , 顏色: 黑色 , 動力:電池

執行正常,子類已經重寫了父類方法,而且不同的子類之間沒有干擾。

7.3.4 super函數

仔細觀察上面方法重載的例子可以發現,兩個子類中print_car()非常相似,只有很少的一部分代碼有區別,本著代碼重用原則,可以使用super函數在子類中調用父類方法,以達到減少子類代碼冗余的目的。

Super是Python的內置函數,可以用來調用父類的方法,這在方法被重載時非常有用。Super函數的語法:super([type[,object-or-type]])。

Super函數有兩種用法:①在單繼承結構中,super可以隱式地返回父類。②支持多繼承,這也是除Python外幾乎目前所有編程語言中唯一一種能做到合理使用多繼承的方式,super使得開發人員可以很好地解決菱形繼承問題。不管哪種用法,super的調用都類似下面形式:

    class C(B):
    def method(self, arg):
        super().method(arg)    #等同于: super(C, self).method(arg)

注意

super這種不傳參數的用法只能用在類方法中,Python解釋器會自動填充參數。

第二個參數object-or-type一般都是self。

了解了以上知識后,使用super修改上面代碼:

7.3.5 訪問權限

前面例子中所有類的屬性與方法都是公有的,也就是說,子類可以沒有限制地使用基類的任何成員。但是有時還需要對類成員的訪問權限加以控制,如只允許類內部使用,或者只允許類本身和子類使用。


□ 類的私有屬性如下。


__private_attrs:以兩個下畫線開頭,不能在類的外部使用,在類內部使用時:self.__ private_attrs。


□ 類的私有方法:


__private_methods:以兩個下畫線開頭,不能在類的外部使用,在類內部使用時:self.__private_methods。

讀者可通過下面的例子看看私有屬性與公有屬性的區別:

訪問公有變量與私有變量:

可見在類外部是不能訪問私有變量的,但是其實Python的私有變量是一個偽私有變量,使用dir函數(dir是Python的內置函數,可以用來查看對象的所有屬性與方法)查看實例at:

從輸出結果可見,at包含了一個屬性_AccessTest__private,這個屬性其實就是我們前面定義的__private屬性,這是Python的名稱修飾(name mangling)功能對__private的重命名。我們繼續訪問這個_AccessTest__private屬性,看看它是不是真的等于__private呢?

    >>> print(at._AccessTest__private)
    1

果然符合預期。

主站蜘蛛池模板: 宁乡县| 宝清县| 江源县| 隆安县| 沭阳县| 陇川县| 咸宁市| 合山市| 景宁| 新密市| 南华县| 广灵县| 霸州市| 焦作市| 临桂县| 灵寿县| 大洼县| 健康| 叶城县| 米林县| 汾西县| 南昌市| 绥棱县| 广灵县| 那曲县| 汕尾市| 新巴尔虎左旗| 长葛市| 芜湖县| 神池县| 贺兰县| 无为县| 隆子县| 黄山市| 响水县| 锡林浩特市| 望都县| 盐源县| 连平县| 纳雍县| 固安县|