<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                在OOP程序設計中,當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。 比如,我們已經編寫了一個名為`Animal`的class,有一個`run()`方法可以直接打印: ~~~ class Animal(object): def run(self): print('Animal is running...') ~~~ 當我們需要編寫`Dog`和`Cat`類時,就可以直接從`Animal`類繼承: ~~~ class Dog(Animal): pass class Cat(Animal): pass ~~~ 對于`Dog`來說,`Animal`就是它的父類,對于`Animal`來說,`Dog`就是它的子類。`Cat`和`Dog`類似。 繼承有什么好處?最大的好處是子類獲得了父類的全部功能。由于`Animial`實現了`run()`方法,因此,`Dog`和`Cat`作為它的子類,什么事也沒干,就自動擁有了`run()`方法: ~~~ dog = Dog() dog.run() cat = Cat() cat.run() ~~~ 運行結果如下: ~~~ Animal is running... Animal is running... ~~~ 當然,也可以對子類增加一些方法,比如Dog類: ~~~ class Dog(Animal): def run(self): print('Dog is running...') def eat(self): print('Eating meat...') ~~~ 繼承的第二個好處需要我們對代碼做一點改進。你看到了,無論是`Dog`還是`Cat`,它們`run()`的時候,顯示的都是`Animal is running...`,符合邏輯的做法是分別顯示`Dog is running...`和`Cat is running...`,因此,對`Dog`和`Cat`類改進如下: ~~~ class Dog(Animal): def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...') ~~~ 再次運行,結果如下: ~~~ Dog is running... Cat is running... ~~~ 當子類和父類都存在相同的`run()`方法時,我們說,子類的`run()`覆蓋了父類的`run()`,在代碼運行的時候,總是會調用子類的`run()`。這樣,我們就獲得了繼承的另一個好處:多態。 要理解什么是多態,我們首先要對數據類型再作一點說明。當我們定義一個class的時候,我們實際上就定義了一種數據類型。我們定義的數據類型和Python自帶的數據類型,比如str、list、dict沒什么兩樣: ~~~ a = list() # a是list類型 b = Animal() # b是Animal類型 c = Dog() # c是Dog類型 ~~~ 判斷一個變量是否是某個類型可以用`isinstance()`判斷: ~~~ >>> isinstance(a, list) True >>> isinstance(b, Animal) True >>> isinstance(c, Dog) True ~~~ 看來`a`、`b`、`c`確實對應著`list`、`Animal`、`Dog`這3種類型。 但是等等,試試: ~~~ >>> isinstance(c, Animal) True ~~~ 看來`c`不僅僅是`Dog`,`c`還是`Animal`! 不過仔細想想,這是有道理的,因為`Dog`是從`Animal`繼承下來的,當我們創建了一個`Dog`的實例`c`時,我們認為`c`的數據類型是`Dog`沒錯,但`c`同時也是`Animal`也沒錯,`Dog`本來就是`Animal`的一種! 所以,在繼承關系中,如果一個實例的數據類型是某個子類,那它的數據類型也可以被看做是父類。但是,反過來就不行: ~~~ >>> b = Animal() >>> isinstance(b, Dog) False ~~~ `Dog`可以看成`Animal`,但`Animal`不可以看成`Dog`。 要理解多態的好處,我們還需要再編寫一個函數,這個函數接受一個`Animal`類型的變量: ~~~ def run_twice(animal): animal.run() animal.run() ~~~ 當我們傳入`Animal`的實例時,`run_twice()`就打印出: ~~~ >>> run_twice(Animal()) Animal is running... Animal is running... ~~~ 當我們傳入`Dog`的實例時,`run_twice()`就打印出: ~~~ >>> run_twice(Dog()) Dog is running... Dog is running... ~~~ 當我們傳入`Cat`的實例時,`run_twice()`就打印出: ~~~ >>> run_twice(Cat()) Cat is running... Cat is running... ~~~ 看上去沒啥意思,但是仔細想想,現在,如果我們再定義一個`Tortoise`類型,也從`Animal`派生: ~~~ class Tortoise(Animal): def run(self): print('Tortoise is running slowly...') ~~~ 當我們調用`run_twice()`時,傳入`Tortoise`的實例: ~~~ >>> run_twice(Tortoise()) Tortoise is running slowly... Tortoise is running slowly... ~~~ 你會發現,新增一個`Animal`的子類,不必對`run_twice()`做任何修改,實際上,任何依賴`Animal`作為參數的函數或者方法都可以不加修改地正常運行,原因就在于多態。 多態的好處就是,當我們需要傳入`Dog`、`Cat`、`Tortoise`……時,我們只需要接收`Animal`類型就可以了,因為`Dog`、`Cat`、`Tortoise`……都是`Animal`類型,然后,按照`Animal`類型進行操作即可。由于`Animal`類型有`run()`方法,因此,傳入的任意類型,只要是`Animal`類或者子類,就會自動調用實際類型的`run()`方法,這就是多態的意思: 對于一個變量,我們只需要知道它是`Animal`類型,無需確切地知道它的子類型,就可以放心地調用`run()`方法,而具體調用的`run()`方法是作用在`Animal`、`Dog`、`Cat`還是`Tortoise`對象上,由運行時該對象的確切類型決定,這就是多態真正的威力:調用方只管調用,不管細節,而當我們新增一種`Animal`的子類時,只要確保`run()`方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則: 對擴展開放:允許新增`Animal`子類; 對修改封閉:不需要修改依賴`Animal`類型的`run_twice()`等函數。 繼承還可以一級一級地繼承下來,就好比從爺爺到爸爸、再到兒子這樣的關系。而任何類,最終都可以追溯到根類object,這些繼承關系看上去就像一顆倒著的樹。比如如下的繼承樹: ![class-inheritance](http://www.liaoxuefeng.com/files/attachments/001390363904103deecc02634aa4406a41692237be32861000/0) ### 靜態語言 vs 動態語言 對于靜態語言(例如Java)來說,如果需要傳入`Animal`類型,則傳入的對象必須是`Animal`類型或者它的子類,否則,將無法調用`run()`方法。 對于Python這樣的動態語言來說,則不一定需要傳入`Animal`類型。我們只需要保證傳入的對象有一個`run()`方法就可以了: ~~~ class Timer(object): def run(self): print('Start...') ~~~ 這就是動態語言的“鴨子類型”,它并不要求嚴格的繼承體系,一個對象只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。 Python的“file-like object“就是一種鴨子類型。對真正的文件對象,它有一個`read()`方法,返回其內容。但是,許多對象,只要有`read()`方法,都被視為“file-like object“。許多函數接收的參數就是“file-like object“,你不一定要傳入真正的文件對象,完全可以傳入任何實現了`read()`方法的對象。 ### 小結 繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫。 動態語言的鴨子類型特點決定了繼承不像靜態語言那樣是必須的。 ### 參考源碼 [animals.py](https://github.com/michaelliao/learn-python3/blob/master/samples/oop_basic/animals.py)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看