一、繼承父類屬性和方法
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面向對象編程系列文章兩週一更!
文章未經博主同意,禁止轉載!