Python面向對象繼承案例


面向對象三大特性

  封裝 根據 職責 將 屬性 和 方法 封裝 到一個抽象的  中

  繼承實現代碼的重用,相同的代碼不需要重復的編寫

  多態 不同的對象調用相同的方法,產生不同的執行結果,增加代碼的靈活度


 

1、單繼承

1.1、 繼承的概念、語法和特點

繼承的概念子類 擁有 父類 的所有 方法 和 屬性

class Animal:

    def eat(self):
        print("")

    def drink(self):
        print("")

    def run(self):
        print("")

    def sleep(self):
        print("")


class Dog:

    def eat(self):
        print("")

    def drink(self):
        print("")

    def run(self):
        print("")

    def sleep(self):
        print("")

    def bark(self):
        print("汪汪叫")

# 創建一個對象 - 狗對象
wangcai = Dog()

wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
不使用用繼承開發動物和狗

1.1.1、 繼承的語法

class 類名(父類名):

    pass
class Animal:

    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):

    # 子類擁有父類的所有屬性和方法
    # def eat(self):
    #     print("吃")
    #
    # def drink(self):
    #     print("喝")
    #
    # def run(self):
    #     print("跑")
    #
    # def sleep(self):
    #     print("睡")

    def bark(self):
        print("汪汪叫")

# 創建一個對象 - 狗對象
wangcai = Dog()

wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
使用繼承開發動物和狗

  子類 繼承自 父類,可以直接 享受 父類中已經封裝好的方法,不需要再次開發

  子類 中應該根據 職責,封裝 子類特有的屬性和方法

1.1.2、 專業術語

  Dog 類是 Animal 類的子類Animal 類是 Dog 類的父類Dog 類從 Animal 類繼承

  Dog 類是 Animal 類的派生類Animal 類是 Dog 類的基類Dog 類從 Animal 類派生

1.1.3、繼承的傳遞性

  C 類從 B 類繼承,B 類又從 A 類繼承

  那么 C 類就具有 B 類和 A 類的所有屬性和方法

子類 擁有 父類 以及 父類的父類 中封裝的所有 屬性 和 方法

class Animal:

    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):

    def bark(self):
        print("汪汪叫")


class XiaoTianQuan(Dog):

    def fly(self):
        print("我會飛")


class Cat(Animal):

    def catch(self):
        print("抓老鼠")

# 創建一個哮天犬的對象
xtq = XiaoTianQuan()

xtq.fly()
xtq.bark()
xtq.eat()

xtq.catch()
繼承的傳遞性

提問:哮天犬 能夠調用 Cat 類中定義的 catch 方法嗎?

答案:不能,因為 哮天犬 和 Cat 之間沒有 繼承 關系。

1.2 方法的重寫

  子類 擁有 父類 的所有 方法 和 屬性

  子類 繼承自 父類,可以直接 享受 父類中已經封裝好的方法,不需要再次開發

應用場景

  當 父類 的方法實現不能滿足子類需求時,可以對方法進行 重寫(override)

    

重寫 父類方法有兩種情況:

  ①覆蓋 父類的方法

  ②對父類方法進行 擴展

1.2.1、覆蓋父類的方法

  如果在開發中,父類的方法實現 和 子類的方法實現完全不同

  就可以使用 覆蓋 的方式,在子類中重新編寫 父類的方法實現

    具體的實現方式,就相當於在 子類中 定義了一個 和父類同名的方法並且實現

   重寫之后,在運行時,只會調用 子類中重寫的方法,而不再會調用 父類封裝的方法

class Animal:

    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):

    def bark(self):
        print("汪汪叫")


class XiaoTianQuan(Dog):

    def fly(self):
        print("我會飛")

    def bark(self):
        print("叫得跟神一樣...")


xtq = XiaoTianQuan()

# 如果子類中,重寫了父類的方法
# 在使用子類對象調用方法時,會調用子類中重寫的方法
xtq.bark()
覆蓋父類方法 

1.2.2、對父類方法進行 擴展

  如果在開發中,子類的方法實現 中 包含父類的方法實現

  父類原本封裝的方法實現 是 子類方法的一部分,就可以使用 擴展 的方式

  在子類中重寫 父類的方法,在需要的位置使用 super().父類方法 來調用父類方法的執行,代碼其他的位置針對子類的需求,編寫 子類特有的代碼實現

調用父類方法的另外一種方式(知道)

  在python2.x中如果需要調用父類的方法,還可以使用以下方式: 

父類名.方法(self)

  這種方式,目前在 Python 3.x 還支持這種方式

  這種方法 不推薦使用,因為一旦 父類發生變化,方法調用位置的 類名 同樣需要修改

提示

  在開發時,父類名 和 super() 兩種方式不要混用

  如果使用 當前子類名 調用方法,會形成遞歸調用,出現死循環

1.3 父類的 私有屬性 和 私有方法

  子類對象 不能 在自己的方法內部,直接 訪問 父類的 私有屬性 或 私有方法

  子類對象 可以通過 父類 的 公有方法間接 訪問到 私有屬性 或 私有方法

示例

  

  • B 的對象不能直接訪問 __num2 屬性
  • B 的對象不能在 demo 方法內訪問 __num2 屬性
  • B 的對象可以在 demo 方法內,調用父類的 test 方法
  • 父類的 test 方法內部,能夠訪問 __num2 屬性和 __test 方法
class A:

    def __init__(self):

        self.num1 = 100
        self.__num2 = 200

    def __test(self):
        print("私有方法 %d %d" % (self.num1, self.__num2))


class B(A):

    def demo(self):

        # 1. 在子類的對象方法中,不能訪問父類的私有屬性
        # print("訪問父類的私有屬性 %d" % self.__num2)

        # 2. 在子類的對象方法中,不能調用父類的私有方法
        # self.__test()
        pass

# 創建一個子類對象
b = B()
print(b)

b.demo()

# 在外界不能直接訪問對象的私有屬性/調用私有方法
# print(b.__num2)
# b.__test()
父類的私有屬性和私有方法
class A:

    def __init__(self):

        self.num1 = 100
        self.__num2 = 200

    def __test(self):
        print("私有方法 %d %d" % (self.num1, self.__num2))

    def test(self):
        print("父類的公有方法 %d" % self.__num2)

        self.__test()


class B(A):

    def demo(self):

        # 1. 在子類的對象方法中,不能訪問父類的私有屬性
        # print("訪問父類的私有屬性 %d" % self.__num2)

        # 2. 在子類的對象方法中,不能調用父類的私有方法
        # self.__test()

        # 3. 訪問父類的公有屬性
        print("子類方法 %d" % self.num1)

        # 4. 調用父類的公有方法
        self.test()
        pass

# 創建一個子類對象
b = B()
print(b)

b.demo()
# 在外界訪問父類的公有屬性/調用公有方法
# print(b.num1)
# b.test()

# 在外界不能直接訪問對象的私有屬性/調用私有方法
# print(b.__num2)
# b.__test()
父類的公有方法
class Person:
    def __init__(self, name, age):
        self.name = name
        self.__age = age
        print('父類的構造方法被調用')

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        self.__age = age

     #  成員方法
    def show(self):
        print("父類的show")


class Worker(Person):
    def __init__(self,name,age,num):
        # 在子類的構造函數中調用父類的構造方法
        # 方式一:
        super().__init__(name, age)
        print('子類的構造函數被調用了')
        self.num = num

        # # 方式二:
        # Person.__init__(name,age)


class Doctor(Person):
    def __init__(self, name, age, num):
        super().__init__(name, age)
        self.num = num

    def show(self):
        print("Doctor show")


p1 = Person('Tom', 23)
p1.show()   # >> 父類的show
print(p1.age)

# 創建子類對象
w1 = Worker('Jack', 21, 2)


# 子類對象可以調用父類對象中未被私有化的方法
w1.show()  # >>父類的show

#子類訪問父類私有屬性,只需要父類中提供@property裝飾的方法【get和set方法】
print(w1.age)

print(p1.name)  #>> tom


d1 = Doctor("lili", 26, 2)
d1.show()   #>>Doctor show
print(d1.name)

繼承的特點:

​   a.子類的獨享可以直接訪問父類中未被私有化的屬性,訪問私有化屬性時,需要在父類提供get和set方法

​   b.子類對象可以調用父類中未被私有化的方法

​   c.父類對象無法訪問子類對象的屬性和方法

優缺點:

​ 優點:

​   可以簡化代碼,減少冗余

​   可以提高代碼的維護性

​   可以提高代碼的安全性

​   繼承是多態的前提

​ 缺點:

​   耦合和內聚被用來描述類與類之間的關系,一般高內聚低耦合說明代碼比較好

  ​ 繼承關系中耦合性相對較高【如果修改父類,則所有子類需求都會改變】 

02.多繼承 

概念

  子類 可以擁有 多個父類,並且具有 所有父類 的 屬性 和 方法

  例如:孩子 會繼承自己 父親 和 母親 的 特性

語法

class 子類名(父類名1, 父類名2...)
    pass
class A:

    def test(self):
        print("test 方法")


class B:

    def demo(self):
        print("demo 方法")


class C(A, B):
    """多繼承可以讓子類對象,同時具有多個父類的屬性和方法"""
    pass


# 創建子類對象
c = C()

c.test()
c.demo()

2.1 多繼承的使用注意事項

問題的提出

  如果 不同的父類 中存在 同名的方法子類對象 在調用方法時,會調用 哪一個父類中的方法呢?

  注意:開發時,應該盡量避免這種容易產生混淆的情況! —— 如果 父類之間 存在 同名的屬性或者方法,應該 盡量避免 使用多繼承

  

Python 中的 MRO —— 方法搜索順序(知道)

  Python 中針對  提供了一個 內置屬性 __mro__ 可以查看 方法 搜索順序

  MRO 是 method resolution order,主要用於 在多繼承時判斷 方法、屬性 的調用 路徑  

print(C.__mro__)  

  在搜索方法時,是按照 __mro__ 的輸出結果 從左至右 的順序查找的

  如果在當前類中 找到方法,就直接執行,不再搜索

  如果 沒有找到,就查找下一個類 中是否有對應的方法,如果找到,就直接執行,不再搜索

  如果找到最后一個類,還沒有找到方法,程序報錯

class A:

    def test(self):
        print("A --- test 方法")

    def demo(self):
        print("A --- demo 方法")

class B:

    def test(self):
        print("B --- test 方法")

    def demo(self):
        print("B --- demo 方法")


class C(B, A):
    """多繼承可以讓子類對象,同時具有多個父類的屬性和方法"""
    pass


# 創建子類對象
c = C()

c.test()
c.demo()

# 確定C類對象調用方法的順序
print(C.__mro__)
多繼承的注意事項
class Dog(object):

    def __init__(self, name):
        self.name = name

    def game(self):
        print("%s 蹦蹦跳跳的玩耍..." % self.name)


class XiaoTianDog(Dog):

    def game(self):
        print("%s 飛到天上去玩耍..." % self.name)


class Person(object):

    def __init__(self, name):
        self.name = name

    def game_with_dog(self, dog):

        print("%s 和 %s 快樂的玩耍..." % (self.name, dog.name))

        # 讓狗玩耍
        dog.game()


# 1. 創建一個狗對象
# wangcai = Dog("旺財")
wangcai = XiaoTianDog("飛天旺財")

# 2. 創建一個小明對象
xiaoming = Person("小明")

# 3. 讓小明調用和狗玩的方法
xiaoming.game_with_dog(wangcai)

>>

    小明 和 飛天旺財 快樂的玩耍...
    飛天旺財 飛到天上去玩耍...

class Father(object):
    def __init__(self, money):
        self.money = money

    def play(self):
        print("playing")


class Mother(object):
    def __init__(self, food):
        self.food = food

    def play(self):
        print("eatting")


#如果多個父類存在重名函數,按照父類參數列表中的順序調用
class Child(Father, Mother):
    def __init__(self, money, food, score):
        Mother.__init__(self, food)
        Father.__init__(self, money)
        self.score = score


f1 = Father(100000)
m1 = Mother("apple")
c1 = Child(100, "emn...", 59)
c1.play()       #>>playing
print(c1.money)

補充:新式類與舊式(經典)類

  object是python為所有對象提供的基類,提供了一些內置屬性和方法,可以使用dir函數查看。

  • 新式類:以 object 為基類的類,推薦使用
  • 經典類:不以 object 為基類的類,不推薦使用

  • 在 Python 3.x 中定義類時,如果沒有指定父類,會 默認使用 object 作為該類的 基類 —— Python 3.x 中定義的類都是 新式類

  • 在 Python 2.x 中定義類時,如果沒有指定父類,則不會以 object 作為 基類

  新式類和經典類在多繼承時,會影響到方法的搜索順序

  為了保證編寫的代碼能夠同時在 Python 2.x 和 Python 3.x 運行!
  今后在定義類時,如果沒有父類,建議統一繼承自 object 

class A(object):
    pass

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM