1. 程式人生 > >Python之旅11:面向物件進階遍

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 執行 【方法一】 時,過程如下:

  1. 根據當前物件中的 類物件指標 找到類中的方法
  2. 將物件 obj1 當作引數傳給 方法的第一個引數 self 

二、類的成員

類的成員可以分為三大類:欄位、方法和屬性