【Python入門】12.物件導向程式設計之 三大特徵:封裝、繼承和多型 & 鴨子型別是什麼?...
*寫在前面:為了更好的學習python,博主記錄下自己的學習路程。本學習筆記基於廖雪峰的Python教程,如有侵權,請告知刪除。歡迎與博主一起學習Pythonヽ( ̄▽ ̄)ノ *
目錄
物件導向程式設計
封裝
• 封裝資料
• 呼叫封裝資料
繼承
• 多繼承
• Mixin
多型
鴨子型別
物件導向程式設計
物件導向程式設計有三大特徵,分別是封裝、繼承和多型。
封裝
封裝,顧名思義是將資料封裝到某處,在要用的時候再從某處呼叫。
封裝資料
首先定義一個class(類)
class Animal(object):
def __init__(self,name,age):
self.name = name
self.age = age
然後定義建立一個對應的Instance(例項)
dog = Animal('dog',3)
事實上,在建立該例項的同時,就把資料'dog'和3分別封裝到dog的name和age屬性中,這個過程就是資料封裝。
呼叫封裝資料
呼叫封裝資料有兩種方法,一種是通過物件直接呼叫:
>>>dog.name
dog
>>>dog.age
3
另一種是通過在類的內部定義方法來間接呼叫::
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self): #再建立一個方法來呼叫資料
print('name = %s' % self.name)
print('age = %d' % self.age)
>>>dog.detail()
name = dog
age = 3
這樣的話,從外部看Animal類,只需要知道建立例項時給出name和age,至於如何列印,都在Animal內部定義,這樣就把這些資料和邏輯封裝起來,可以簡單呼叫卻不用知道內部的細節。
繼承
在OOP程式設計中,在定義一個class時,可以從另一個class繼承下來,新的class稱為子類或派生類,被繼承的class稱為父類或超類、基類。舉些簡單的例子:
父類 | 子類 |
---|---|
動物 | 狗,貓,熊 |
文具 | 筆,尺子,橡皮 |
這跟類與例項的關係有點像,只不過在這裡兩者都是類。
繼承最大的好處就是子類可以沿用父類裡面的全部功能,比如我們定義一個繼承Animal型別的Dog類。
class Dog(Animal):
def __init__(self,name,age):
self.name = name
self.age = age
這時候我們再建立一個Dog對應的例項dog,此時dog就可以用Animal的方法了。
>>>dog = Dog('pupy',1)
>>>dog.detail()
name = pupy
age = 2
可見在Dog中並沒有定義detail這一方法,而例項dog是繼承了Animal裡面的detail方法。
多繼承
在java和C#中只能繼承一類,而在Python中允許繼承多類,舉個簡單的例子。
class D(object): #定義一個父類D
def say(self):
print('D.say')
class B(D): #B類繼承D類
def say(self):
print('B.say')
class C(D): #C類繼承D類
def say(self):
print('C.say')
class A(B,C): #A類繼承B類和C類
這個時候A就繼承了兩個類,B與C。現在有個問題是當A的例項在.say()的時候,是執行哪個類的say方法呢?看一下輸出結果:
>>>a = A()
>>>a.say()
B.say
在多繼承中,在呼叫方法時,會按照廣度優先的方式去查詢對應的方法。
在本例中,先查詢A類有沒有say(),然後查詢B類,再查詢C類,最後查詢D類,如果都沒找到則報錯,如果找到了則不會再查詢後面的類。
(這裡插一個小知識點,在Python2及以前的版本中,存在經典類和新式類兩種類,經典類在定義時沒有繼承object,而新式類則有繼承。現在使用的便是新式類,採用廣度優先的方式查詢,而經典類採用深度優先的方式查詢,放在上例中就是A-B-D-C的順序。而在Python3即之後的版本中,已經沒有經典類和新式類的說法了,都預設採用新式類,即使在定義時沒有繼承object)
Mixin
多繼承的好處是能簡化多層次的繼承關係。這裡舉廖雪峰官方網站裡面的一個例子來說明。
假設我們要實現以下4種動物:
Dog - 狗狗;
Bat - 蝙蝠;
Parrot - 鸚鵡;
Ostrich - 鴕鳥。
按照哺乳動物Mammal和鳥類Bird歸類,我們可以設計出這樣的類的層次:
┌───────────────┐
│ Animal │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Mammal │ │ Bird │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Bat │ │ Parrot │ │ Ostrich │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
這時如果我們要新增“能跑”“能飛”的分類,層次就會變成這樣:
┌───────────────┐
│ Animal │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Mammal │ │ Bird │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ MRun │ │ MFly │ │ BRun │ │ BFly │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Bat │ │ Ostrich │ │ Parrot │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
如果還要再分“寵物類”和“非寵物類”,那麼類的數量會呈指數增長,這樣明顯不行。這時候就需要用到多重繼承了。
先定義一個主要類層,還是按哺乳類和鳥類設計:
class Animal(object):
pass
#大類:
class Mammal(Animal):
pass
class Bird(Animal):
pass
再額外定義Runnable類和Flyable類:
class Runnable(object):
def run(self):
print('Running...')
class Flyable(object):
def fly(self):
print('Flying...')
這時候再定義動物小類,如果是能跑的哺乳類動物,則同時繼承上面兩個類即可:
class Dog(Mammal, Runnable):
pass
class Bat(Mammal, Flyable):
pass
這種“混入”額外功能的多重繼承設計通常稱之為Mixin。MixIn的目的就是給一個類增加多個功能。為了更好體現出繼承關係,通常在把額外功能的類名改為XxxMixin,像這樣:
class RunnableMixin(object):
def run(self):
print('Running...')
class FlyableMixin(object):
def fly(self):
print('Flying...')
class Dog(Mammal, RunnableMixin):
pass
class Bat(Mammal, FlyableMixin):
pass
多型
多型,是指一個物件具有多種形態,或者說一個型別具有多種型別的能力。
在理解多型之前,我們要對資料型別再做一點說明。實際上,在我們定義一個類的時候,也就是定義了一種資料型別。
x = int(5) #x是int型別
y = list(1) #y是list型別
a = Animal() #a是Animal型別
dog = Dog() #dog是Dog型別
接下來我們定義一個父類及其一些子類:
class Animal(object):
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
class Pig(Animal):
pass
由於繼承的關係,Dog的例項dog不僅是Dog型別,還是Animal型別,可以通過isinstance()來判斷:
>>>dog = Dog()
>>>isinstance(dog, Dog)
True
>>>isinstance(dog, Animal)
True
所以,在繼承關係中,如果一個例項的資料型別是某個子類,那它的資料型別也可以被看做是父類。
接下來我們在類中增加一些方法,再定義一個函式。
class Animal(object):
def __init__(self,name): #在建立例項時,繫結一個name屬性
self.name = name
def run(self,name): #定義一個方法,列印想要的內容
print(self.name + ' is running!')
class Dog(Animal):
pass
class Cat(Animal):
pass
class Pig(Animal):
pass
def run1(animal): #定義一個接受animal型別的函式
print('wow!')
animal.run(animal.name) #執行animal中的run方法
horse = Animal('lin') #建立一個Animal類的例項
dog = Dog('pupy') #建立一個Dog類的例項
cat = Cat('miumiu') #建立一個Cat類的例項
執行run1函式,傳入例項物件horse:
>>>run1(horse)
wow!
lin is running!
當我們傳入例項物件dog和cat時同樣可以執行。
>>>run1(dog)
wow!
pupy is running!
>>>run1(cat)
wow!
miumiu is running!
這就是所講的多型。對於函式run1而言,傳入的animal可以是Animal型別,也可以是Dog型別或是Cat型別的例項物件。
看到這裡,你或許會有疑問:animal只不過是一個函式的引數,難道不可以接受任何型別的資料?
是的,你沒有錯,這就是動態語言的特點。所以實質上,對於Python這種動態語言來說,並沒有多型這一說法,而是崇尚鴨子型別。
鴨子型別
Duck typing,鴨子型別,是動態型別的一種風格。
這個概念的名字來源於由James Whitcomb Riley提出的鴨子測試,“鴨子測試”可以這樣表述:
“當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”
接上面的例子,我們再定義一個類,這個類不繼承Animal。
class Xxxx(object):
def __init__(self, name):
self.name = name
def run(self, name):
print(self.name + ' is running!')
aaaa = Xxxx('aaaa')
把例項物件aaaa傳入run1函式,依舊可以執行。
>>>run1(aaaa)
wow!
aaaa is running!
run1函式並沒有判斷傳入的引數是否為animal型別,只是判斷傳入的引數是否有run的方法,如果有,則可以執行。
這就是動態語言的鴨子型別,run1函式並不在乎傳入的aaaa是不是動物(鴨子),它只是run(走)起來像animal(鴨子),那麼把它就可以被當成動物(鴨子)。
以上就是本節的全部內容,感謝你的閱讀。
有任何問題與想法,歡迎評論與吐槽。
和博主一起學習Python吧( ̄▽ ̄)~*
相關文章
- 物件導向三大特徵(封裝/繼承/多型)物件特徵封裝繼承多型
- 物件導向的三個基本特徵是:封裝、繼承、多型物件特徵封裝繼承多型
- [JAVA] Java物件導向三大特徵:封裝、繼承、多型Java物件特徵封裝繼承多型
- js:物件導向程式設計,帶你認識封裝、繼承和多型JS物件程式設計封裝繼承多型
- 物件導向三大特性-----封裝、繼承、多型物件封裝繼承多型
- JAVA物件導向基礎--封裝 繼承 多型Java物件封裝繼承多型
- 【Java】瘋狂Java基礎(一)——物件導向的特徵:繼承、封裝和多型Java物件特徵繼承封裝多型
- 物件導向的三大特徵,封裝、繼承、多型的個人理解以及程式碼分析物件特徵封裝繼承多型
- 專案過程中如何使用物件導向的特徵:繼承,封裝,多型,介面物件特徵繼承封裝多型
- Go語言結構體(struct)物件導向程式設計進階篇(封裝,繼承和多型)Go結構體Struct物件程式設計封裝繼承多型
- 封裝、繼承和多型封裝繼承多型
- 物件導向:封裝,多型物件封裝多型
- java核心思想物件導向三大特性(封裝,繼承,多型)Java物件封裝繼承多型
- Java入門教程九(封裝繼承多型)Java封裝繼承多型
- ~~核心程式設計(五):物件導向——多繼承~~程式設計物件繼承
- 物件導向基礎(1)--繼承 多型 重構物件繼承多型
- Python - 物件導向程式設計 - 三大特性之繼承Python物件程式設計繼承
- python極簡教程07:封裝、多型和繼承Python封裝多型繼承
- go物件導向思想:封裝、繼承、多肽Go物件封裝繼承
- Python:多型、協議和鴨子型別Python多型協議型別
- java封裝繼承以及多型(含程式碼)Java封裝繼承多型
- go語言中的封裝,繼承和多型Go封裝繼承多型
- java-物件導向程式設計--多型Java物件程式設計多型
- 黑馬程式設計師——物件導向(上)— 類、物件、封裝、繼承、構造方法程式設計師物件封裝繼承構造方法
- 全網最適合入門的物件導向程式設計教程:17 類和物件的Python實現-鴨子型別與“file-like object“物件程式設計Python型別Object
- 面向2-封裝、繼承、多型封裝繼承多型
- JS物件導向程式設計(四):繼承JS物件程式設計繼承
- java-物件導向程式設計--繼承Java物件程式設計繼承
- JavaScript物件導向程式設計——Array型別JavaScript物件程式設計型別
- 物件導向之繼承物件繼承
- Python 繼承 和 多型Python繼承多型
- Python 物件導向程式設計之封裝的藝術Python物件程式設計封裝
- Cris 的 Scala 筆記整理(八):物件導向中級-繼承和多型筆記物件繼承多型
- 什麼是繼承?Python繼承的特徵有哪些?繼承Python特徵
- aardio 實現封裝繼承多型封裝繼承多型
- 物件導向程式設計和`GP`泛型程式設計物件程式設計泛型
- 全網最適合入門的物件導向程式設計教程:31 Python的內建資料型別-物件Object和型別Type物件程式設計Python資料型別Object
- 物件導向之_繼承概念物件繼承