Python之旅11:面向物件進階遍
本章內容:
- 面向物件三大特性(封裝、繼承、多型)
- 類的成員
一、面向物件三大特性
面向物件的三大特性是指:封裝、繼承和多型。
1、封裝
封裝,顧名思義就是將內容封裝到某個地方,以後再去呼叫被封裝在某處的內容。
所以,在使用面向物件的封裝特性時,需要:
- 將內容封裝到某處
- 從某處呼叫被封裝的內容
第一步:將內容封裝到某處
self 是一個形式引數,當執行 obj1 = Foo('wupeiqi', 18 ) 時,self 等於 obj1
當執行 obj2 = Foo('alex', 78 ) 時,self 等於 obj2
第二步:從某處呼叫被封裝的內容
呼叫被封裝的內容時,有兩種情況:
- 通過物件直接呼叫
- 通過self間接呼叫
class Foo: def __init__(self, name, age): self.name = name self.age = age obj = Foo('nick', 18) print (obj.name) # 直接呼叫obj物件的name屬性 print (obj.age) # 直接呼叫obj物件的age屬性 obj2 = Foo('jenny', 21) print (obj2.name) # 直接呼叫obj2物件的name屬性 print (obj2.age) # 直接呼叫obj2物件的age屬性 class Foo: def __init__(self, name, age): self.name = name self.age = age def detail(self): print (self.name) print (self.age) obj = Foo('nick', 18) obj.detail() # Python預設會將obj傳給self引數,即:obj.detail(obj),所以,此時方法內部的 self = obj,即:self.name 是 nick ;self.age 是 18 obj2 = Foo('jenny', 21) obj2.detail() # Python預設會將obj2傳給self引數,即:obj1.detail(obj2),所以,此時方法內部的 self = obj2,即:self.name 是 jenny ; self.age 是 21
綜上所述,對於面向物件的封裝來說,其實就是使用構造方法將內容封裝到 物件 中,然後通過物件直接或者self間接獲取被封裝的內容。
2、繼承
對於面向物件的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。
注:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不同而已。
# 繼承 # 基類 class Animals: def __init__(self,name): self.name = name def eat(self): print(self.name,"吃") # 派生類 class dog(Animals): def tell(self): print("旺旺旺") dog = dog("大黃") dog.tell() dog.eat()
多繼承:
1、Python的類可以繼承多個類,Java和C#中則只能繼承一個類
2、Python的類如果繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先和廣度優先
- 當類是經典類時,多繼承情況下,會按照深度優先方式查詢
- 當類是新式類時,多繼承情況下,會按照廣度優先方式查詢
經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 當前類或者父類繼承了object類,那麼該類便是新式類,否則便是經典類(python3屬於新式類)。
經典類多繼承:
class D:
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 執行bar方法時
# 首先去A類中查詢,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去D類中找,如果D類中麼有,則繼續去C類中找,如果還是未找到,則報錯
# 所以,查詢順序:A --> B --> D --> C
# 在上述查詢bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
經典類多繼承
新式類多繼承:
class D(object):
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 執行bar方法時
# 首先去A類中查詢,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去C類中找,如果C類中麼有,則繼續去D類中找,如果還是未找到,則報錯
# 所以,查詢順序:A --> B --> C --> D
# 在上述查詢bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
新式類多繼承
經典類:首先去A類中查詢,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去D類中找,如果D類中麼有,則繼續去C類中找,如果還是未找到,則報錯
新式類:首先去A類中查詢,如果A類中沒有,則繼續去B類中找,如果B類中麼有,則繼續去C類中找,如果C類中麼有,則繼續去D類中找,如果還是未找到,則報錯
注意:在上述查詢過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
3、多型
多型的作用是什麼呢?我們知道,封裝可以隱藏實現細節,使得程式碼模組化;繼承可以擴充套件已存在的程式碼模組(類);它們的目的都是為了——程式碼重用。而多型則是為了實現另一個目的——介面重用!多型的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的例項的某一屬性時的正確呼叫。
Pyhon不支援Java和C#這一類強型別語言中多型的寫法,但是原生多型,其Python崇尚“鴨子型別”。
第一:Python虛擬碼實現Java或C#的多型
class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
# 由於在Java或C#中定義函式引數時,必須指定引數的型別
# 為了讓Func函式既可以執行S1物件的show方法,又可以執行S2物件的show方法,所以,定義了一個S1和S2類的父類
# 而實際傳入的引數是:S1物件和S2物件
def Func(F1 obj):
"""Func函式需要接收一個F1型別或者F1子類的型別"""
print obj.show()
s1_obj = S1()
Func(s1_obj) # 在Func函式中傳入S1類的物件 s1_obj,執行 S1 的show方法,結果:S1.show
s2_obj = S2()
Func(s2_obj) # 在Func函式中傳入Ss類的物件 ss_obj,執行 Ss 的show方法,結果:S2.show
第二:Python “鴨子型別”
class F1:
pass
class S1(F1):
def show(self):
print ('S1.show')
class S2(F1):
def show(self):
print ('S2.show')
def Func(obj):
print (obj.show())
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
第三:通過Python模擬的多型
class Animal:
def __init__(self, name): # Constructor of the class
self.name = name
def talk(self): # Abstract method, defined by convention only
raise NotImplementedError("Subclass must implement abstract method")
class Cat(Animal):
def talk(self):
return 'Meow!'
class Dog(Animal):
def talk(self):
return 'Woof! Woof!'
animals = [Cat('Missy'),
Dog('Lassie')]
for animal in animals:
print (animal.name + ': ' + animal.talk())
類和物件在記憶體中是如何儲存的?
類以及類中的方法在記憶體中只有一份,而根據類建立的每一個物件都在記憶體中需要存一份,大致如下圖:
如上圖所示,根據類建立物件時,物件中除了封裝 name 和 age 的值之外,還會儲存一個類物件指標,該值指向當前物件的類。
當通過 obj1 執行 【方法一】 時,過程如下:
- 根據當前物件中的 類物件指標 找到類中的方法
- 將物件 obj1 當作引數傳給 方法的第一個引數 self
二、類的成員
類的成員可以分為三大類:欄位、方法和屬性