Python面向對象中的繼承、多態和封裝

Python面向對象中的繼承、多態和封裝

1、面向對象的三大特性

  1. 封裝:把不少數據封裝到⼀個對象中,把固定功能的代碼封裝到⼀個代碼塊, 函數,對象, 打包成模塊。 這都屬於封裝思想。
  2. 繼承:⼦類能夠⾃動擁有⽗類中除了私有屬性外的其餘全部內容。 說⽩了, ⼉⼦能夠隨便⽤爹的東⻄。
  3. 多態: 同⼀個對象, 多種形態。在Python中到處是多態,由於在Python中一個變量能夠是多種形態。

2、封裝

封裝,顧名思義,就是將某些東西給封裝起來,之後想要使用的時候再去調用。python

因此,在使用面向對象的封裝特性時須要:① 將內容封裝到某處 ② 調用時從某處取出來算法

封裝分爲兩部分:函數

  1. 廣義上的封裝:實例化一個對象,給對象空間封裝一些屬性。
  2. 狹義上的封裝:私有制。私有成員:私有靜態屬性,私有方法,私有對象屬性。

咱們先看廣義上的封裝:3d

  1. 將內容封裝到某處code

    class MyClass:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    # 在實例化對象時,會自動執行__init__方法,將對象空間傳遞給self這個位置參數
    obj = MyClass("oldniu", 20)
    # 將"oldniu" 和 20 分別封裝到obj(self)的name和age屬性中
  2. 從某處調用封裝的內容對象

    調用被封裝的內容時,有兩種方式:① 經過對象直接調用 ② 經過self間接調用blog

    1. 經過對象直接調用繼承

      class MyClass:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
      
      obj = MyClass("oldniu", 20)
      print(obj.name)     # 經過obj對象直接調用裏面的name屬性
      print(obj.age)      # 經過obj對象直接調用裏面的age屬性
    2. 經過self間接調用ip

      class MyClass:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def func(self):
              print(self.name)    # 使用self間接調用obj對象中的name屬性
              print(self.age)     # 使用self間接調用obj對象中的age屬性
      
      
      obj = MyClass("oldniu", 20)
      obj.func()  # 對象執行方法時Python會默認將obj傳給參數self

    綜上所述,對於面向對象廣義上的封裝來講,其實就是使用初始化方法將內容封裝到 對象 中,而後經過對象直接或者self間接獲取被封裝的內容。開發

    咱們再看狹義上的封裝

    1. 私有靜態屬性

      class MyClass:
          __gender = "男"  # 私有靜態屬性
      
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def get_gender(self):
              return self.__gender    # 在類的內部能夠訪問私有靜態屬性
      
      # print(MyClass.__gender)     # 類名不能直接訪問私有靜態屬性
      
      obj = MyClass("oldniu", 20)
      # print(obj.__gneder)         # 對象不能訪問私有靜態屬性
      
      print(obj.get_gender())     # 男

      對於私有靜態字段來講,只能在本類中內部訪問,類的外部,子類均不可訪問。

      tips:其實在類的外部能夠經過使用_MyClass__gender訪問私有靜態屬性,可是通常咱們不會去使用。

    2. 私有方法

      class MyClass:
      
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def __func(self):
              print("666666")
      
          def func(self):
              self.__func() 類內部訪問靜態方法
      
      
      obj = MyClass("dogfa", 20)
      obj.func()
      # 私有方法和私有靜態字段同樣,只能在本類中內部訪問,類的外部和子類均不課訪問。

    3、繼承

    什麼是繼承?⼦類能夠⾃動擁有⽗類中除了私有屬性外的其餘全部內容就是繼承。

    1. 繼承的優勢
      1. 提升代碼的複用性
      2. 提升代碼的維護性
      3. 使類與類之間發生關聯
    2. 繼承的分類
      1. 單繼承
      2. 多繼承

    咱們先來看單繼承

    1. 類名,對象執行父類方法

      # 定義一個Animal父類
      class Animal:
          type_name = "動物類"
      
          def __init__(self, name):
              self.name = name
      
          def eat(self):
              print("吃吃吃...")
      
      # 繼承於Animal類
      class Dog(Animal):
          pass
      
      # 類名調用父類的屬性和方法
      print(Dog.type_name)
      Dog.eat(111)
      
      # 對象調用父類的屬性和方法
      dog = Dog("二哈")   # 調用父類的__init__方法實例化對象
      print(dog.name)
      print(dog.type_name)
      dog.eat()
      print(dog.__dict__)
    2. 執行順序

      1. 實例化對象時必須執行__init__方法,類中沒有,從父類找,父類沒有,從object類中找。
      2. 先要執行本身類中的eat方法,本身類沒有才能執行父類中的方法。
    3. 同時執行類及父類方法

      方法一:

      若是想執行父類的func方法,這個方法而且子類中夜用,那麼就在子類的方法中寫上:

      父類.func(對象,其餘參數)

      class Animal:
          type_name = "動物類"
      
          def __init__(self, name, age, gender):
              self.name = name
              self.age = age
              self.gender = gender
      
          def eat(self):
              print("吃吃吃...")
      
      class Bird(Animal):
          def __init__(self, name, age, gender, wing):        
              Animal.__init__(self, name, age, gender)    # 執行了父類的__init__方法進行初始化
              self.wing = wing    # 給對象封裝wing屬性
      
      obj = Bird("鸚鵡", 3, "雌", "翅膀")
      print(obj.__dict__)
      # {'name': '鸚鵡', 'age': 3, 'gender': '雌', 'wing': '翅膀'}

      方法二:

      利用super,super().func(參數)

      class Animal:
          type_name = "動物類"
      
          def __init__(self, name, age, gender):
              self.name = name
              self.age = age
              self.gender = gender
      
          def eat(self):
              print("吃吃吃...")
      
      class Bird(Animal):
          def __init__(self, name, age, gender, wing):
              super().__init__(name, age, gender)    # 使用super會自動將self傳給父類
              self.wing = wing    # 給對象封裝wing屬性
      
      obj = Bird("鸚鵡", 3, "雌", "翅膀")
      print(obj.__dict__)
      # {'name': '鸚鵡', 'age': 3, 'gender': '雌', 'wing': '翅膀'}

    再來看看多繼承

    在多繼承中要補充一點,在這裏要注意類的種類。類能夠分爲經典類新式類

    新式類:繼承object類的類稱爲新式類。在Python3中默認都是繼承object類,因此默認全部類都是新式類。

    經典類:不繼承object類的類稱爲經典類。在Python2中默認使用經典類,固然也能夠本身手動繼承object類來達到變成經典類的目的。

    1. 經典類的多繼承

      在Python的繼承體系中, 咱們能夠把類與類繼承關係化成⼀個樹形結構的圖。

      class A:
          pass
      class B(A):
          pass
      class C(A):
          pass
      class D(B, C):
          pass
      class E:
          pass
      class F(D, E):
          pass
      class G(F, D):
          pass
      class H:
          pass
      class Foo(H, G):
          pass

      對付這種mro畫圖就能夠:

      繼承關係圖已經有了,那如何進⾏查找呢? 記住⼀個原則, 在經典類中採⽤的是深度優先遍歷⽅案。 什麼是深度優先,就是⼀條路走到頭, 而後再回來, 繼續找下⼀個。

      類的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C

    2. 新式類的繼承

      1. mro序列

        MRO是一個有序列表L,在類被建立時就計算出來。
        通用計算公式爲:

        mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
        (其中Child繼承自Base1, Base2)

        若是繼承至一個基類:class B(A)
        這時B的mro序列爲

        mro( B ) = mro( B(A) )
        = [B] + merge( mro(A) + [A] )
        = [B] + merge( [A] + [A] )
        = [B,A]

        若是繼承至多個基類:class B(A1, A2, A3 …)
        這時B的mro序列

        mro(B) = mro( B(A1, A2, A3 …) )
        = [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
        = ...

        計算結果爲列表,列表中至少有一個元素即類本身,如上述示例[A1,A2,A3]。merge操做是C3算法的核心。

      2. 表頭和表位

        表頭:
          列表的第一個元素

        表尾:
          列表中表頭之外的元素集合(能夠爲空)

        示例
          列表:[A, B, C]
          表頭是A,表尾是B和C

      3. 列表之間的+操做

        +操做:

        [A] + [B] = [A, B]
        (如下的計算中默認省略)
        ---------------------

        merge操做示例:

        如計算merge( [E,O], [C,E,F,O], [C] )
        有三個列表 :  ①      ②          ③
        
        1 merge不爲空,取出第一個列表列表①的表頭E,進行判斷                              
           各個列表的表尾分別是[O], [E,F,O],E在這些表尾的集合中,於是跳過當前當前列表
        2 取出列表②的表頭C,進行判斷
           C不在各個列表的集合中,於是將C拿出到merge外,並從全部表頭刪除
           merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] )
        3 進行下一次新的merge操做 ......
        ---------------------

        計算mro(A)方式:

        mro(A) = mro( A(B,C) )
        
        原式= [A] + merge( mro(B),mro(C),[B,C] )
        
          mro(B) = mro( B(D,E) )
                 = [B] + merge( mro(D), mro(E), [D,E] )  # 多繼承
                 = [B] + merge( [D,O] , [E,O] , [D,E] )  # 單繼承mro(D(O))=[D,O]
                 = [B,D] + merge( [O] , [E,O]  ,  [E] )  # 拿出並刪除D
                 = [B,D,E] + merge([O] ,  [O])
                 = [B,D,E,O]
        
          mro(C) = mro( C(E,F) )
                 = [C] + merge( mro(E), mro(F), [E,F] )
                 = [C] + merge( [E,O] , [F,O] , [E,F] )
                 = [C,E] + merge( [O] , [F,O]  ,  [F] )  # 跳過O,拿出並刪除
                 = [C,E,F] + merge([O] ,  [O])
                 = [C,E,F,O]
        
        原式= [A] + merge( [B,D,E,O], [C,E,F,O], [B,C])
            = [A,B] + merge( [D,E,O], [C,E,F,O],   [C])
            = [A,B,D] + merge( [E,O], [C,E,F,O],   [C])  # 跳過E
            = [A,B,D,C] + merge([E,O],  [E,F,O])
            = [A,B,D,C,E] + merge([O],    [F,O])  # 跳過O
            = [A,B,D,C,E,F] + merge([O],    [O])
            = [A,B,D,C,E,F,O]
        ---------------------

3、多態

多態:同一個對象,多種形態。

因爲Python是弱類型語言, 在定義變量和傳參的時候能夠是任意形態的值,因此Python默認支持多態。

鴨子類型:你看起來像鴨子,那就是鴨子。

對相同的功能設定了相同的名字,這樣方便開發,這兩個方法就能夠互成爲鴨子類型。好比list、tuple、str都有index()方法,這就是互稱爲鴨子類型。

相關文章
相關標籤/搜索