把一組數據結構和處理它們的方法組成對象(object),把相同行爲的對象歸納爲類(class),通過類的封裝(encapsulation)隱藏內部細節,通過繼承(inheritance)實現類的特化(specialization)和泛化(generalization),通過多態(polymorphism)實現基於對象類型的動態分派。
面向對象思想有三大要素:封裝、繼承和多態
封裝:封裝是一個概念,它的含義是把方法、屬性、事件集中到一個統一的類中,並對使用者屏蔽其中的細節問題。數據被保護在抽象數據類型的內部,儘可能地隱藏內部的細節,只保留一些對外接口使之與外部發生聯繫。
繼承:如果兩個類存在繼承關係,則子類會自動繼承父類的方法和變量,在子類中可以調用父類的方法和變量,如果想要在子類裏面做一系列事情,應該放在父類無參構造器裏面。在java中,只允許單繼承,也就是說一個類最多隻能顯示地繼承於一個父類。但是一個類卻可以被多個類繼承,也就是說一個類可以擁有多個子類。
多態:多態性是指相同的操作可作用於多種類型的對象上並獲得不同的結果。不同的對象,收到同一消息可以產生不同的結果,這種現象稱爲多態性。
類和對象
簡單的說,類是對象的藍圖和模板,而對象是類的實例。這個解釋雖然有點像用概念在解釋概念,但是從這句話我們至少可以看出,類是抽象的概念,而對象是具體的東西。在面向對象編程的世界中,一切皆爲對象,對象都有屬性和行爲,每個對象都是獨一無二的,而且對象一定屬於某個類(型)。當我們把一大堆擁有共同特徵的對象的靜態特徵(屬性)和動態特徵(行爲)都抽取出來後,就可以定義出一個叫做“類”的東西。
定義類
在Python中可以使用class關鍵字定義類,然後在類中通過之前學習過的函數來定義方法,這樣就可以將對象的動態特徵描述出來,代碼如下所示。
class Student(object):
# __init__是一個特殊方法用於在創建對象時進行初始化操作
# 通過這個方法我們可以爲學生對象綁定name和age兩個屬性
def __init__(self,name,age):
self.name=name
self.age=age
def study(self,course_name):
print('%s正在學習%s' %(self.name,course_name))
# PEP 8要求標識符的名字用全小寫多個單詞用下劃線連接
# 但是很多程序員和公司更傾向於使用駝峯命名法(駝峯標識)
def wacht_av(self):
if self.age < 18:
print('%s只能觀看《熊出沒》' %self.name)
else:
print('%s正在觀看島國愛情動作片.' % self.name)
說明:寫在類中的函數,我們通常稱之爲(對象的)方法,這些方法就是對象可以接收的消息。
創建和使用對象
當我們定義好一個類之後,可以通過下面的方式來創建對象並給對象發消息。
def main():
stu1=Student('張蘇',38)
stu1.study('python程序設計')
stu1.wacht_av()
stu2=Student('張三',15)
stu2.study('思想品德')
stu2.wacht_av()
if __name__ == '__main__':
main()
訪問可見性問題
對於上面的代碼,有C++、Java、C#等編程經驗的程序員可能會問,我們給Student對象綁定的name和age屬性到底具有怎樣的訪問權限(也稱爲可見性)。因爲在很多面向對象編程語言中,我們通常會將對象的屬性設置爲私有的(private)或受保護的(protected),簡單的說就是不允許外界訪問,而對象的方法通常都是公開的(public),因爲公開的方法就是對象能夠接受的消息。在Python中,屬性和方法的訪問權限只有兩種,也就是公開的和私有的,如果希望屬性是私有的,在給屬性命名時可以用兩個下劃線作爲開頭,下面的代碼可以驗證這一點。
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
# AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
# AttributeError: 'Test' object has no attribute '__foo'
print(test.__foo)
if __name__ == "__main__":
main()
但是,Python並沒有從語法上嚴格保證私有屬性或方法的私密性,它只是給私有的屬性和方法換了一個名字來“妨礙”對它們的訪問,事實上如果你知道更換名字的規則仍然可以訪問到它們,下面的代碼就可以驗證這一點。之所以這樣設定,可以用這樣一句名言加以解釋,就是“We are all consenting adults here”。因爲絕大多數程序員都認爲開放比封閉要好,而且程序員要自己爲自己的行爲負責。
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo)
if __name__ == "__main__":
main()
強化練習
練習1:設計一個Circle(圓)類
包括圓心位置,半徑、顏色等屬性。編寫構造方法和其他方法,計算周長和麪積
from math import pi
class Circle:
def __init__(self,color,point,radius):
self.color=color
self.point=point
self.radius=radius
def area(self):
return pi*self.radius*self.radius
def perimeter(self):
return 2*pi*self.radius
circle=Circle(color="red",point="18",radius=10)
area1=circle.area()
per1=circle.perimeter()
color1=circle.color
point1=circle.point
print(area1,per1,color1,point1)
練習2:定義一個類描述數字時鐘
from time import sleep
class Clock(object):
def __init__(self,hour=0,minute=0,second=0):
self.hour=hour
self.minute=minute
self.second=second
def run(self):
self.second+=1
if self.second==60:
self.second=0
self.minute+=1
if self.minute==60:
self.minute=0
self.hour+=1
if self.hour == 24:
self.hour=0
def __str__(self):
return '%02d:%02d:%02d' %(self.hour,self.minute,self.second)
def main():
clock=Clock(23,59,58)
while True:
print(clock)
sleep(1)
clock.run()
if __name__ == '__main__':
main()
練習3:定義和使用矩形類
class Rect(object):
def __init__(self,width=0,height=0):
self.__width=width
self.__height=height
def perimeter(self):
return (self.__height+self.__width)*2
def area(self):
return (self.__width*self.__height)
def __str__(self):
return '矩形[%f,%f]' %(self.__width,self.__height)
def __del__(self):
print('銷燬矩形對象')
if __name__ == '__main__':
rect1=Rect()
print(rect1)
print(rect1.perimeter())
print(rect1.area())
rect2=Rect(4,5)
print(rect2)
print(rect2.perimeter())
print(rect2.area())