Python面向對象編程(五)

一、繼承父類屬性和方法

1. 繼承

       面向對象的編程帶來的好處之一是代碼的重用,實現這種重用方法之一是通過繼承機制。繼承(Inheritance)是兩個類或多個類之間的父子關係,子類繼承了父類的所有公有數據屬性和方法,並且可以通過編寫子類的代碼擴充子類的功能。想象一下,如果人類可以做到子女繼承父母的所有才學並加以擴展,那麼人類的發展至少是現在的數萬倍。繼承實現了數據屬性和方法的重用,減少了代碼的冗餘度。
      在程序中,繼承描述的是事物之間的所屬關係,例如貓和狗都屬於動物,程序中便可以描述爲貓和狗繼承自動物;同理,波斯貓和巴釐貓都繼承自貓,而沙皮狗和斑點狗都繼承自狗。
 
       特定狗種類繼承狗類,狗類繼承動物類,狗類編寫了描述所有狗種共有的行爲和方法,而特定狗種類則增加了該狗種特有的行爲。不過繼承也有一定弊端,可能父類對於子類也有一定特殊的地方,如某種特定狗種不具有絕大部分狗種的行爲,當程序員沒有理清類間的關係時,可能使得子類具有了不該有的方法。另外,如果繼承鏈太長的話,任何一點小的變化都會引起一連串變化,使用的繼承要注意控制繼承鏈的規模。
      在Python中繼承有以下一些特點。
      (1) 在繼承中基類初始化方法__init__不會被自動調用。如果希望子類調用基類的__init__方法,需要在子類的__init__方法顯示調用它。這與C++和C#區別很大。
      (2) 在調用基類的方法時,需要加上基類的類名前綴,且帶上self參數變量。注意在類中調用在該類定義的方法是不需要self參數的。
      (3) Python總是首先查找對應類的方法,如果在子類沒有對應的方法,Python纔會在繼承鏈的基類中按順序查找。
      (4) 在Python繼承中,子類不能訪問基類的私有成員。
      最後一次修改類Cat的代碼。
>>>class Cat():
...    def __init__(self):
...        self.name = '貓'
...        self.age = 4
...        self.info = [self.name,self.age]
...        self.index = -1
...    def run(self):
...        print( self.name,'--在跑')
...    def getName(self):
...        return self.name
...    def getAge(self):
...        return self.age
...    def __iter__(self):
...        print('名字 年齡')
...        return self
...    def next(self):
...        if self.index == len(self.info)-1:
...            raise StopIteration
...        self.index += 1
...        return self.info[self.index]
>>>class Bosi(Cat):
...   def setName(self, newName):
...        self.name = newName
...   def eat(self):
...        print( self.name,'--在喫')
...        
>>>bs = Bosi()                              # 創建對象
>>>print( 'bs的名字爲:',bs.name)               # 繼承父類的屬性和方法
bs的名字爲: 貓
>>>print( 'bs的年齡爲:',bs.age)
bs的年齡爲: 4
>>>print(bs.run())
貓 --在跑
>>>bs.setName('波斯貓')                      # 子類的屬性和方法
>>>bs.eat()
波斯貓 --在喫
>>>iterator = iter(bs.next,1)                    # 迭代輸出父類的屬性
>>>for info in iterator:
...    print(info)

4
       定義了Bosi類的父類Cat,將貓共有的屬性和方法都放到父類中,子類僅僅需要向父類傳輸數據屬性即可。這樣做可以很輕鬆地定義其他基於Cat類的子類。因爲假如有數百隻貓,使用繼承的方法大大減少了代碼量,且當需要對全部貓整體修改時僅需要修改Cat類即可。Bosi類的__init__函數中顯示調用了Cat類的__init__函數,並向父類傳輸數據,這裏注意要加self參數。
在繼承裏面不能繼承父類的私有屬性,所以也不用擔心父類和子類出現因繼承造成的重名情況。爲了能更清晰地講述這個問題,這裏再舉一個例子。
>>>class animal():
...    def __init__(self,age):
...        self.__age = age
...    def print2(self):
...        print(self.__age)
>>>class dog(animal):
...    def __init__(self,age):
...        animal.__init__(self,age)
...    def print2(self):
...        print(self.__age)

>>>a_animal = animal(10)
>>>a_animal.print2()
10
>>>a_dog = dog(10)
>>>a_dog.print2()                                 # 程序報錯
AttributeError: 'dog' object has no attribute '_dog__age'

2. 多繼承

      如果有多個父類,則父類名需要全部都寫在括號裏,這種情況稱爲多繼承,格式爲Class子類名(父類名1,父類名2,•••)。
>>>class A(object):                                 # 定義一個父類
...    def __init__(self):
...        print ("   ->Input A")
...        print ("   <-Output A")
>>>class B(A):                                   # 定義一個子類
...    def __init__(self):
...        print ("  -->Input B")
...        A.__init__(self)
...        print ("  <--Output B")
>>>class C(A):                                  # 定義另一個子類
...    def __init__(self):
...        print (" --->Input C")
...        A.__init__(self)
...        print (" <---Output C")
>>>class D(B, C):                                # 定義一個子類
...    def __init__(self):
...        print ("---->Input D")
...        B.__init__(self)
...        C.__init__(self)
...        print ("<----Output D")

>>>d = D()                   # Python中是可以多繼承的,父類中的方法、屬性,子類會繼承。
---->Input D
  -->Input B
   ->Input A
   <-Output A
  <--Output B
 --->Input C
   ->Input A
   <-Output A
 <---Output C
<----Output D
>>>issubclass(C,B)            # 判斷一個類是不是另一個類的子孫類
False
>>>issubclass(C,A)
True
       實現繼承之後,子類將繼承父類的屬性,也可以使用內建函數issubclass函數來判斷一個類是不是另一個類的子孫類,前項參數爲子類,後項參數爲父類。

二、掌握其他方法

       面向對象的三大特性是指重載、封裝和多態。

1. 重載

       所謂重載,就是子類中,有一個和父類相同名字的方法,在子類中的方法會覆蓋掉父類中同名的方法。
>>>class Cat:
...    def sayHello(self):
...        print("喵-----1")
>>>class Bosi(Cat):
...    def sayHello(self):
...        print("喵喵----2")

>>>bosi = Bosi()
>>>bosi.sayHello()                  # 子類中的方法會覆蓋掉父類中同名的方法
喵喵----2

2. 封裝

       既然Cat實例本身就擁有這些數據,要訪問這些數據,就沒有必要從外面的函數去訪問,可以直接在Cat類的內部定義訪問數據的函數。這樣,就把數據給“封裝”起來了。
       封裝(Encapsulation)就是將抽象得到的數據和行爲(或功能)相結合,形成一個有機的整體(即類);封裝的目的是增強安全性和簡化編程,使用者不必瞭解具體的實現細節,而只是通過外部接口,特定的訪問權限來使用類即可。簡而言之,就是將內容封裝到某個地方,以後再去調用被封裝在某處的內容。

3. 多態

       多態性(Polymorphism)是對象通過他們共同的屬性和動作來操作及訪問,允許將父對象設置成和一個或多個它的子對象相等的技術,比如Car=Land_Rover。多態性使得能夠利用同一類(父類)類型的指針來引用不同類的對象,以及根據所引用對象的不同,以不同的方式執行相同的操作。

      Python是動態語言,可以調用實例方法,不檢查類型,只要方法存在,參數正確就可以調用,這就是與靜態語言(例如Java)最大的差別之一。表明了動態(運行時)綁定的存在,允許重載或運行時類型確定和驗證。

三、練習

       在創建的Car類上產生子類Land_Rover,要使子類Land_Rover擁有2個父類屬性(品牌、顏色)和兩個自帶屬性(車輪數、廢氣渦輪增壓),然後輸出子類屬性。

參考代碼:

class Car():
    def __init__(self,brand, newWheelNum, newColor,T):
        self.brand = brand
        self.wheelNum = newWheelNum
        self.color = newColor
        self.T = T                              # T爲廢氣渦輪增壓
        self.info = [self.brand,self.wheelNum,self.color,self.T]
        self.index = -1
    def getBrand(self):
        return self.brand
    def getNewheelnum(self):
        return self.wheelNum
    def getNewcolor(self):
        return self.color
    def getT(self):
        return self.T
    def __iter__(self):
        print('品牌 車輪數 顏色 廢氣渦輪增壓')
        return self
    def next(self):
        if self.index == 3:
            raise StopIteration
        self.index += 1
        return self.info[self.index]

class Land_Rover(Car):
    def __init__(self,brand, newColor):
        self.brand = brand
        self.wheelNum = 4
        self.color = newColor
        self.T = 3
        Car.__init__(self,self.brand,self.wheelNum,self.color,self.T)

Luxury_car = Car('BMW',4, 'green',2.4)               # 創建對象
print(Luxury_car.getNewcolor())                    # 訪問屬性
iterator = iter(Luxury_car.next,1)                    # 調用迭代函數
for info in iterator:
    print(info)

 

 

Python面向對象編程系列文章兩週一更!

 

文章未經博主同意,禁止轉載!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章