1. 程式人生 > >python學習筆記(七):面向對象編程、類

python學習筆記(七):面向對象編程、類

單繼承 之前 麻煩 適合 popu 性別 實例 派生 ext

一、面向對象編程

面向對象--Object Oriented Programming,簡稱oop,是一種程序設計思想。在說面向對象之前,先說一下什麽是編程範式,編程範式你按照什麽方式來去編程,去實現一個功能。舉個例子,你要做飯,可以用電磁爐,也可以用煤氣竈。不同的編程範式本質上代表對各種類型的任務采取的不同的解決問題的思路,兩種最重要的編程範式分別是面向過程編程和面向對象編程。

提到面向對象,就不得不提到另一種編程思想,面向過程;什麽是面向過程呢,面向過程的思想是把一個項目、一件事情按照一定的順序,從頭到尾一步一步地做下去,先做什麽,後做什麽,一直到結束。這種思想比較好理解,其實這也是一個人做事的方法,我們之前編程的思想也都是使用這種思想。這種編程思想,只要前面有一個步驟變了,那麽後面的就也要變,後面維護起來比較麻煩,這樣的編程思想,我們在寫一些簡單的小程序、只執行一次的腳本時可以使用。而面向對象呢,面向對象的思想是把一個項目、一件事情分成更小的項目,或者說分成一個個更小的部分,每一部分負責什麽方面的功能,最後再由這些部分組合而成為一個整體。這種思想比較適合多人的分工合作,就像一個大的機關,分各個部門,每個部門分別負責某樣職能,各個部門可以充分發揮自己的特色,只要符合一定前提就行了。

舉個例子:比如剛才說的一個大的機關,要做某一個項目,從面向過程的思想來說,應該是這樣分析的,先怎麽樣,再怎麽樣,最後怎麽樣。第一樣應該如何完成,第二樣應該如何完成等等。等到每一步驟都完成,項目也就完成了。而面向對象的思想則應該是這樣想的,這個項目是由幾個部分組成的,我們就做好分工,成立一個部門來做一個部分的功能,另一個部門來做另一個部分。各個部門可以不用理解其他部門的事,只要完成自己那一部分的事情就OK了。

二、面向對象的特性

類:class

類,對比現實世界來說就是一個種類,一個模型。

一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。

在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法。

對象:object

對象,也就是指模型造出來的具體的東西。

一個對象即是一個類的實例化後實例,一個類必須經過實例化後方可在程序中調用,一個類可以實例化多個對象,每個對象亦可以有不同的屬性,就像人類是指所有人,每個人是指具體的對象,人與人之前有共性,亦有不同。

實例化:

初始化一個類,造了一個對象。把一個類變成一個具體的對象的過程,叫做實例化。

封裝:

把一些功能的實現細節不對外暴露,類中對數據的賦值、內部調用對外部用戶是透明的,這使類變成了一個膠囊或容器,裏面包含著類的數據和方法。

比如說造的一個人,你把他身體內部的什麽心肝脾肺腎都封裝起來了,其他人都看不到,你直接找這個人。

繼承:

一個類可以派生出子類,在這個父類裏定義的屬性、方法自動被子類繼承。比如說你繼承了你父親的姓。

python3中多繼承都是廣度優先,python2中經典類的多繼承是深度優先,新式類的多繼承是按照廣度優先的。

繼承是為了代碼的重用

多態:

對不同類的對象發出相同的消息將會有不同的行為。比如,你的老板讓所有員工在九點鐘開始工作, 他只要在九點鐘的時候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術人員說:“開始技術工作”, 因為“員工”是一個抽象的事物, 只要是員工就可以開始工作,他知道這一點就行了。至於每個員工,當然會各司其職,做各自的工作。

多態就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再通過這個抽象的事物, 與不同的具體事物進行對話。

一種接口,多種實現。

三、面向對象的好處

對於編程語言的初學者來講,OOP不是一個很容易理解的編程方式,大家雖然都按老師講的都知道OOP的三大特性是繼承、封裝、多態,並且大家也都知道了如何定義類、方法等面向對象的常用語法,但是一到真正寫程序的時候,還是很多人喜歡用函數式編程來寫代碼,特別是初學者,很容易陷入一個窘境就是“我知道面向對象,我也會寫類,但我依然沒發現在使用了面向對象後,對我們的程序開發效率或其它方面帶來什麽好處,因為我使用函數編程就可以減少重復代碼並做到程序可擴展了,為啥子還用面向對象?”對於此,我個人覺得原因應該還是因為你沒有充分了解到面向對象能帶來的好處。

無論用什麽形式來編程,我們都要明確記住以下原則:

寫重復代碼是非常不好的低級行為

你寫的代碼需要經常變更

開發正規的程序跟那種寫個運行一次就扔了的小腳本一個很大不同就是,你的代碼總是需要不斷的更改,不是修改bug就是添加新功能等,所以為了日後方便程序的修改及擴展,你寫的代碼一定要遵循易讀、易改的原則(專業數據叫可讀性好、易擴展)。

如果你把一段同樣的代碼復制、粘貼到了程序的多個地方以實現在程序的各個地方調用,這個功能,那日後你再對這個功能進行修改時,就需要把程序裏多個地方都改一遍,這種寫程序的方式是有問題的,因為如果你不小心漏掉了一個地方沒改,那可能會導致整個程序的運行都 出問題。 因此我們知道 在開發中一定要努力避免寫重復的代碼,否則就相當於給自己再挖坑。

還好,函數的出現就能幫我們輕松的解決重復代碼的問題,對於需要重復調用的功能,只需要把它寫成一個函數,然後在程序的各個地方直接調用這個函數名就好了,並且當需要修改這個功能時,只需改函數代碼,然後整個程序就都更新了。

其實OOP編程的主要作用也是使你的代碼修改和擴展變的更容易,那麽小白要問了,既然函數都能實現這個需求了,還要OOP幹毛線用呢? 呵呵,說這話就像,古時候,人們打仗殺人都用刀,後來出來了槍,它的主要功能跟刀一樣,也是殺人,然後小白就問,既然刀能殺人了,那還要槍幹毛線,哈哈,顯而易見,因為槍能更好更快更容易的殺人。函數編程與OOP的主要區別就是OOP可以使程序更加容易擴展和易更改。

四、類

一些概念:

屬性:屬性就是類裏面的一個變量,有類變量和實例變量,類變量是類在定義的時候就有的,實例變量是在實例化的時候才產生的變量。這個可以理解為,人是一個類,他的名字、年齡、性別就是它的屬性。

方法:方法就是類的功能,也就是定義在類裏面的函數,它實現了某個功能,比如說人有睡覺的功能。

構造函數:什麽是構造函數,就是類在實例化的時候做的某些初始化操作,比如說人,你造一個汽車的時候,它得有顏色、型號等等。

析構函數:析構函數就是這個實例在銷毀的時候做的一些操作。

定義類:

定義類使用class關鍵字,類名一般我們開發的時候首字母要大寫。python中有經典類和新式類,他倆在python3中沒有區別,在python2中經典類在多繼承的時候是深度優先,新式類是廣度優先。python3中統一是廣度優先,這個後面在說繼承的時候會說。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Person(object):#定義類,這個是新式類 nationality = ‘China‘ #類屬性 def __init__(self,name,sex): #構造函數 self.name = name #實例屬性 self.sex =sex def __del__(self):#析構函數 print(‘這個是析構函數‘) def info():#方法 print(‘name is %s sex is %s ‘%(self.name,self.sex)) class Person1:#定義類,這個是經典類 nationality = ‘China‘ #類屬性 def __init__(self,name,sex): #構造函數 self.name = name #實例屬性 self.sex =sex def info():#方法 print(‘name is %s sex is %s ‘%(self.name,self.sex)) niuniu = Person(‘牛牛‘,‘男‘) #實例化,因為在上面的構造函數裏面指定了必須傳name和sex,所以在實例化的時候要把name和sex傳進去 niuniu.info()#調用實例方法

繼承:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class F(object): ‘‘‘ 這個是父類 ‘‘‘ def __init__(self,name,sex): self.name = name self.sex = sex def info():#方法 print(‘name is %s sex is %s ‘%(self.name,self.sex)) class S(F):#繼承F這個類 pass s1 = S(‘牛牛‘,‘男‘)#實例化子類 s1.info()#因為繼承父類,所以父類的方法他都有 那如果在父類裏面有一個方法,子類裏面也有的話,但是子類想重寫一下父類的方法,增加新功能下面這麽寫: class test(): def __init__(self,name): self.name = name class test1(test): def __init__(self,name,age): # test.__init__(self,name)#修改父類的方法,這個是經典類裏面的寫法 super(test1,self).__init__(name)#和上面的效果一樣,這個是新式類裏面的寫法 self.name = name self.age = age test1(‘name‘,‘age‘) 多繼承,上面寫的都是單繼承的,python裏面還支持多繼承,多繼承就是繼承多個父類,在python3中多繼承都是廣度優先的,在python2中有點不同,經典類是深度優先,新式類是廣度優先。 class A: def say(self): print(‘A‘) class B(A): # pass def say(self): print(‘B‘) class C(A): pass # def say(self): # print(‘c‘) class D(C,B): pass s=D() s.say()

多態:python裏面是不直接支持多態的,可以通過別的方法來實現多態。通過下面的例子就可以實現一種接口,多種實現,對於下面的cry方法來說,不管你傳入的是什麽對象,只要有cry的方法,都會去調用它的cry方法,不用再一個個的去調用了。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Animal(object): ‘‘‘ 父類 ‘‘‘ def __init__(self,name): self.name = name class Dog(Animal): ‘‘‘ 狗類,有叫的方法 ‘‘‘ def cry(self): print(‘狗 [%s] 汪汪汪‘%self.name) class Cat(Animal): ‘‘‘ 貓類,有叫的方法 ‘‘‘ def cry(self): print(‘貓 [%s] 喵喵喵‘%self.name) def cry(obj): ‘‘‘ 定義一個函數,去調用傳進來的實例的cry方法 ‘‘‘ obj.cry() d1 = Dog(‘大黃‘)#實例化狗 d2 = Dog(‘小黃‘)#實例化狗 c1 = Cat(‘小白‘)#實例化貓 c2 = Cat(‘小黑‘)#實例化貓 cry(d1)#把對象d1傳進來 cry(d2)#把對象d2傳進來 objs = [d1,d2,c1,c2]#把上面實例化的對象都放到一個list裏面 for obj in objs:#循環統一調用 cry(obj)

私有方法、私有屬性:

什麽是私有,私有就是只有在類裏面可以訪問,實例化之後不可以訪問和調用,有私有方法和私有屬性。私有就把變量名或者函數名前面加上"__"兩個下劃線,其實就是通過私有來實現封裝的。

1 2 3 4 5 6 7 8 9 10 11 12 class Dog(object): __type = ‘狗‘#私有屬性 def cry(self): self.__test()#調用私有方法 print(‘私有屬性%s‘%self.__type) print(‘狗 [%s] 汪汪汪‘%self.name) def __test(self):#私有方法 self.name = ‘test‘# d = D() d.cry()#正常可以調用 d.__type#報錯,因為是私有屬性,在外面不能訪問 d.__test()#報錯,因為是私有方法,在外面不能訪問

靜態方法和類方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Stu(object): country = ‘china‘#類變量 def __init__(self,name): self.name = name @staticmethod #靜態方法,和類本身沒有什麽關系了,就相當於在類裏面定義了一個方法而已 def say(): print(‘xxx‘) @classmethod #靜態方法,和類方法不同的是,它可以使用類變量,必須要傳一個值,代表的就是這個類 def hello(cls): print(cls.country) def hi(self): #這個是實例方法 print(self.name) t = Stu(‘name‘) Stu.hello() Stu.say() t.hi() t.say() t.hello()

python學習筆記(七):面向對象編程、類