Python基礎-第六天-面向對象編程
本篇內容
1.面向對象編程介紹
2.類的定義及各部分的介紹
3.屬性
4.方法
5.面向對象的特性-封裝
6.面向對象的特性-繼承
7.面向對象的特性-多態
8.新式類和經典類
一、面向對象編程介紹
1.編程的原則
無論用什麽編程範式來編程都要記住的原則是,避免寫重復代碼,代碼要易擴展。一定要遵循可讀性好、易擴展的原則。
2.面向對象編程(Object-Oriented Programming)介紹
OOP編程的主要作用是使代碼修改和擴展變的更容易;
面向對象編程是利用類和對象來幫助我們實現功能的。類裏面放的是函數,每一個函數可以為一個功能,不同的是,我們不能在類的外面直接調用函數,必須借助對象來調用函數,而對象是通過實例化類生成的。
在Python中一切皆對象,Python中字符串、列表等等都是一個對象。
每一個對象都有屬性,例如對象的名字、顏色等等都是它的屬性。
每一個對象都有功能(即方法),比如append()就是列表其中一個功能。
在Python中通過“.”來調用對象的功能。
3.面向對象的概念和特性
①概念
●Class(類)
一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法(也可以理解為功能);
●Object(對象)
一個對象即是一個類的實例化後實例,一個類必須經過實例化後方可在程序中調用,一個類可以實例化多個對象,每個對象亦可以有不同的屬性,就像人類是指所有人,每個人是指具體的對象,人與人之間有共性,亦有不同。
通過類生成了對象就叫實例化,對象也可以稱為實例;
②特性
●Encapsulation(封裝)
在類中對數據的賦值、內部調用對外部用戶是透明的、不可見的,這使類變成了一個膠囊或容器,裏面包含著類的數據和方法;
作用:1.防止數據被隨意修改;2.使外部程序不需要關註對象內部的構造或邏輯,只需要通過對象對外提供的接口進行訪問即可;3.避免寫重復代碼;
●Inheritance(繼承)
一個類可以派生出子類,在這個父類裏定義的屬性、方法自動被子類繼承。
通過子類繼承父類的方式,可以用最少代碼量實現不同的對象既具有共同點,又具有不同點。
繼承主要的作用就是避免寫重復代碼;
●Polymorphism(多態)
多態是面向對象的重要特性,簡單點說:“一個接口,多種實現”,指一個基類中派生出了不同的子類,且每個子類在繼承了同樣的方法名的同時又對父類的方法做了不同的實現,這就是同一種事物表現出的多種形態。
對不同類的對象發出相同的消息將會有不同的行為。比如,你的老板讓所有員工在九點鐘開始工作, 他只要在九點鐘的時候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術人員說:“開始技術工作”, 因為“員工”是一個抽象的事物, 只要是員工就可以開始工作,他知道這一點就行了。至於每個員工,當然會各司其職,做各自的工作。
多態允許將子類的對象當作父類的對象使用,某父類型的引用指向其子類型的對象,調用的方法是該子類型的方法。這裏引用和調用方法的代碼編譯前就已經決定了,而引用所指向的對象可以在運行期間動態綁定。
二、類的定義及各部分的介紹
1.類的定義
class Person(object): # 類名中的所有單詞首字母要大寫,采用駝峰命名法(例CamelCase)的書寫方式; # 在類中直接定義的屬性即是公有屬性 nationality = "CN" def __init__(self, name, age): # 構造函數 self.Name = name # 普通屬性,相當於p1.Name = "Jack" self.age = age # 私有屬性外部是不可見的,內部是可見的 self.__money = "1W" def talk(self): # 類的方法函數 print("%s 說的是中文" % self.Name) def get_money(self): # 類的方法函數,這個函數的作用是對外部提供私有屬性只讀訪問接口 # 私有屬性只能在類的內部調用,不能在類的外部調用 self.__money = "2W" print(self.__money) return self.__money def __del__(self): # 析構函數 print("實例: %s消亡了" % self.Name)
①構造函數
__init__這個函數就是構造函數,構造函數中定義了有普通屬性
在實例化時傳遞的參數都傳遞到了構造函數中。如果不需要給構造函數傳遞參數,則不用定義構造函數;
②類的方法函數
定義所有對象都具有的共同功能
類的方法函數是公有的方法,只在類中保存了一份
③公有屬性(也稱為靜態屬性)
在類中直接定義的屬性即是公有屬性,公有屬性和構造函數、類的方法函數是平級的。下面會詳細介紹
④私有屬性
私有屬性只能在內部被調用,不能在外部被調用。
⑤析構函數
析構函數在實例被銷毀後就會自動執行。上面的__del__就是一個析構函數;
使用del刪除對象和程序結束時會銷毀實例,這時析構函數也會執行;
析構函數可以用來做程序的收尾工作,比如關閉打開的文件等;
⑥self的作用
其實self就是實例本身,實例化時python會自動把這個實例本身通過self參數傳進去。self的存在就是為了解決類中在所有的函數中都能訪問對象屬性的問題。
2.實例化
p1 = Person("Jack", 33) # 實例化類生成對象,相當於Person(p1, "Jack", 26) p2 = Person("Eric", 33) # 實例化類生成對象,相當於Person(p2, "Eric", 28)
註意,雖然p1和p2都是通過Person類實例化出來的,但p1和p2是兩個不通的對象。比如,人類代表所有人,但人是具體到每個不同的人,這裏Person類是人類,p1和p2是一個個具體的人。
3.調用對象方法
p1.talk() # 調用人的說話功能,相當於d1.talk(p1)
當類的方法函數需要另外傳參數時,在調用該方法函數時要在括號中寫上參數;
三、屬性
屬性分為:成員屬性、私有屬性和公有屬性(靜態屬性);
1.成員屬性
在實例化時,傳遞給構造函數初始化的屬性稱為成員屬性,又可以稱為成員變量或實例屬性;
上面的Name和age都是成員屬性;
2.私有屬性
私有屬性只能在內部被調用,不能在外部被調用。
私有屬性是在構造函數中定義的;
私有屬性的命名規則是兩個下劃線加變量名,上面的__money是私有屬性;
由於私有屬性只能在內部被調用,當在外部需要調用時怎麽辦?上面的get_money方法函數就是提供外部調用私有屬性的接口,通過return返回私有屬性。
3.公有屬性(靜態屬性)
所有屬於這個類的對象都可以訪問的屬性即是公有屬性
在類中直接定義的屬性即是公有屬性,公有屬性和構造函數、類的方法函數是平級的。上面的nationality就是一個公有屬性;
為什麽需要公有屬性,試想一下,現在有一個需求,需要通過中國類生成所有中國人,生成的每個中國人都是一個獨立的對象,他們有一個共同的國籍屬性都為中國,而實例屬性是封裝在每個對象中,那問題就來了,國籍屬性需要存十三億份,這時使用公有屬性,就只會在類中存一份。
①訪問公有屬性的方法
●通過類直接訪問
print(Person.nationality) # 直接去類中查找,找到了就返回,沒找到就報錯。
●通過對象訪問
# 先去對象中查找,找到了就直接返回,如果沒有找到,再去類中查找,找到了就返回,沒找到就報錯。 print(p1.nationality)
②修改公有屬性的值
●通過類修改
# 通過類修改公有屬性,修改的是保存在類中的公有屬性的值。是全局修改,修改的是所有 Person.nationality = "JP"
●通過對象修改
# 通過對象修改公有屬性,會在對象中直接初始化一個成員屬性,名字為公有屬性的名字,值為要修改成的內容。 p1.nationality = "JP"
四、方法
類的方法分為:普通方法(或動態屬性)、靜態方法
1.普通方法(或動態屬性)
普通方法是公有的方法,如上面的talk、get_money函數就是普通方法,只在類中保存了一份;
普通方法的調用者是對象,要通過對象來調用普通方法;
普通方法至少需要有一個self參數;
2.靜態方法
為什麽需要靜態方法?假如我們創建了一個類,類中沒有構造函數,只有方法,在這樣的情況下,如果要調用類中的方法,就必須先要實例化出對象,然後通過對象來調用類中的方法。那麽這樣幹,就導致了在類和對象都在占用了內存空間的情況下,和單獨用函數來做實現的效果一樣,並且函數占用的內存空間還小一些,這時我們就需要用靜態方法來解決這個問題。
靜態方法允許在不創建對象的情況下直接被調用,就是不需要通過對象來調用,可以直接通過類來調用;上面的
在方法函數上面加上@staticmethod後,方法函數就變成了靜態方法:
@staticmethod def talk(self): # 靜態方法 print("%s 說的是中文" % self.Name) Person.talk() # 通過類直接調用
五、面向對象的特性-封裝
封裝,顧名思義就是將內容封裝到某個地方,以後再去調用被封裝在某處的內容。
封裝在類中的有:公有屬性、構造函數、動態屬性(或普通方法)、靜態方法;
封裝在對象中的有:成員屬性(普通屬性)的值;
在使用面向對象的封裝特性時,需要知道:將內容封裝到某處,就要從某處調用被封裝的內容。
調用被封裝的內容時,有兩種情況:通過對象直接調用、通過self間接調用(在類的方法函數中,需要通過self間接調用被封裝的內容);
六、面向對象的特性-繼承
1.什麽是繼承
面向對象中的繼承和現實生活中的繼承相同,即子可以繼承父的內容。
繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。所以繼承的主要功能就是代碼重用;
通過繼承創建的新類稱為子類或派生類。被繼承的類稱為基類、父類或超類;
2.實現繼承
要實現繼承,可以通過繼承(Inheritance)和組合(Composition)兩種方式來實現。
在某些OOP語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實現繼承多個類,可以通過多重繼承(同時繼承多個類)和多級繼承(父類下面有子類,子類下面還有孫子類)兩種方式來實現。
繼承概念的實現方式主要有兩種:實現繼承、接口繼承。實現繼承是指使用基類的屬性和方法而無需額外編碼的能力;接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力(子類重構父類方法);
3.繼承的語法
●單繼承
class Person(object): # 父類 def __init__(self, name, age): self.name = name self.age = age def talk(self): print("人在說話") class BlackPerson(Person): # 括號中寫所繼承的類 def walk(self): print("黑人在走路") # BlackPerson類繼承了Person類的構造函數和talk方法
所以,對於面向對象的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。
●多繼承
class SchoolMenber(object): """學校成員基類""" member = 0 # 學校成員的總人數 def __init__(self, name, age, sex): """ :param name: 姓名 :param age: 年齡 :param sex: 性別 """"" self.name = name self.age = age self.sex = sex self.enroll() # 只要一實例化就會自動調用註冊功能自動註冊 def enroll(self): """註冊功能""" print("註冊一個新的學校成員: %s" % self.name) SchoolMenber.member += 1 # 每註冊一個新成員,公有屬性member就會加一 def tell(self): """ 打印成員信息""" print("----- info: %s -----" % self.name) for key, value in self.__dict__.items(): print("\t%s: %s" % (key, value)) def __del__(self): """析構函數,在實例銷毀時自動執行""" print("成員: %s被開除了" % self.name) SchoolMenber.member -= 1 class School(object): """學校類""" def open_branch(self, addr): """ 開分校的功能 :param addr: 分校的地址 :return: """"" print("新的分校位於: %s" % addr) class Teacher(SchoolMenber, School): # 括號中寫所繼承的類 """講師類""" def teaching(self): """教學功能""" print("%s講師正在講%s課程" % (self.name, self.course))
當通過Teacher類實例化出的對象調用方法時,先在Teacher類中查找,沒有找到再去SchoolMenber類中查找,最後去School類中找,如果還沒找到就報錯。
4.繼承和重構
有時候我們需要繼承父類中的方法,並且還要在該方法中加點其它東西怎麽辦?
class Person(object): # 父類 def __init__(self, name, age): self.name = name self.age = age def talk(self): print("人在說話") class BlackPerson(Person): # 括號中寫所繼承的類 # 先繼承,再重構 # self把當前這個實例也傳遞進來了,所以self代表當前這個實例 # 實例化時name、age是傳遞給父類的,strength是傳遞給子類的 def __init__(self, name, age, strength): # 繼承Person類中的構造函數 # self是在實例化時將當前實例傳遞進來給子類,子類再將當前這個實例傳遞給父類 # name和age是在實例化時傳遞進來給子類的兩個參數,子類再將這兩個參數傳遞給父類 # 經典類寫法和新式類寫法效果一樣,現在主要寫新式類 # Person.__init__(self, name, age) # 經典類寫法 super(BlackPerson, self).__init__(name, age) # 新式類寫法 self.strength = strength # 子類獨有的屬性 def talk(self): # 繼承Person類中的talk方法函數 # self是在實例化時將當前實例傳遞進來給子類,子類再將當前這個實例傳遞給父類 # Person.talk(self) # 經典類寫法 super(BlackPerson, self).talk() # 新式類寫法 print("黑人在說話") def walk(self): print("黑人在走路")
七、面向對象的特性-多態
多態是面向對象的重要特性,簡單點說:一個接口,多種實現;
接口重用,但表現形式卻不一樣;
python中不直接支持多態,但可以間接實現,崇尚“鴨子類型”。
# 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)
八、新式類和經典類
1.什麽是新式類和經典類
經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 當前類或者父類繼承了object類,那麽該類便是新式類,否則便是經典類。
2.新式類和經典類的繼承順序
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() a.bar()
●Python 2.X的:
經典類(深度優先):首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去D類中找,如果D類中麽有,則繼續去C類中找,如果還是未找到,則報錯
新式類(廣度優先):首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去C類中找,如果C類中麽有,則繼續去D類中找,如果還是未找到,則報錯
●Python 3.X的:
不管是經典類還是新式類,采用的都是廣度優先;
註意:在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了。
本文出自 “12031302” 博客,謝絕轉載!
Python基礎-第六天-面向對象編程