面向對象編程(OOP,Object Oriented Programming)
OOP三個主要的特征:數據封裝、繼承和多態。
所有的程序是由一定的屬性和行為對象組成的,不同的對象的訪問通過函數調用來完成,對象間所有的交流都是通過方法調用,通過對封裝對象數據,提高復用率。
面向對象編程是一種編程方式,此編程方式的落地需要使用 "類" 和 "對象"來實現,所以,面向對象編程其實就是對 "類" 和 "對象" 的使用。
類就是一個模板,模板裡可包含多個函數,函數裡實現一些功能。對象則是根據模板創建的實例,通過實例對象可以執行類中的方法。
class Role (object): def __init__(self,name): self.name = name def buy_weapon(self,weapon): passclass是關鍵字,定義一個類,其名稱為Role,這個類繼承了Python的父類,名為object。在類的下面可以看到有很多函數的定義,但在 class中,函數被稱為方法,所以這裡暫時在一個名為Role類的下面有__init__和buy_weapon方法。
封裝,顧名思義就是將內容封裝到某個地方,以後再去調用被封裝在某處的內容。
所以,在使用面向對象的封裝特性時,需要:1.將內容封裝到某處 2.從某處調用被封裝的內容
class Role (object): #object是父類,繼承父類 def __init__(self,name,role,weapon,life_value): #稱為構造方法,根據類創建對象時自動執行 self.name = name #成員變量,self 代表傳進來的name,在整個類都可以使用這個變量,把一個局部變量變為了全局變量 self.role = role self.weapon = weapon self.life_val = life_value def buy_weapon(self,weapon): print("[%s] is buying [%s]" % (self.name,weapon)) self.weapon = weapon # 把一個抽象的類變成一個具體的過程叫實例化 t1 = Role("Stanley",'Terrorist','Glock',100) #等價於Role(t1,"Stanley",'Terrorist','b11',100),同時將'Stanley'、'Terrorist'、'Glock'和100封裝到t1和self的name、role、weapon、life_value屬性中 p1 = Role("Tom",'Police','Desert Eagle',90) #Role(p1,"Tom",'Police','Desert Eagle',90) print("t1's weapon was [%s] before" % t1.weapon) print("=".center(40,'=')) t1.buy_weapon('AK47') #轉換為Role.buy_weapon(t1,'AK47') print("t1's weapon is [%s] now" % t1.weapon) Result: t1's weapon was [Glock] before ======================================== [Stanley] is buying [AK47] t1's weapon is [AK47] now
第一個方法__init__是初始化構造方法,構造方法的第一個參數永遠是self,表示這個類的對象本身,真正構造對象時,self這個參數不用寫,python編譯器會自己加上去,構造方法的作用就是對self對象進行賦值,如下面傳進來的name賦給self.name,這麼做的目的是因為在class中,每個方法依舊是要遵循函數規則的,在函數下面每個變量都是局部變量,在其他函數中並不能相互調用,如此做就把一個局部變量變為全局變量。
通過self間接調用被封裝的內容:
第二個方法除了編譯器會自己加上去的self參數外,weapon參數接收傳入的變量,打印調用的參數。在print之後self.name含義是將調用者的name賦值給self。
接下來要對p1和t1進行實例化,這是將一個抽象的類變成一個具體的過程,生成了p1和t1兩個角色。t1 = Role("Stanley",'Terrorist','Glock',100),Python解釋器會將此轉換為 Role(t1,"Stanley",'Terrorist','Glock',100),這裡通過解釋器轉換後的t1其實就是self。
現在t1角色進行購買行為,t1.buy_weapon('AK47'),Python解釋器會將此轉換為 Role.buy_weapon(t1,'AK47'),如上所述此時的t1就是self,也可理解為將t1傳入__init__初始化構造方法,就可直接self.name(等價於t1.name)獲取到name的變量了(Stanley)。
綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到對象中,然後通過對象直接或者self間接獲取被封裝的內容。
繼承,面向對象中的繼承和現實生活中的繼承相同,即:子可以繼承父的內容,也可理解為由一般到特殊。
class SchoolMember(object): member_nums = 0 def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex self.enroll() def enroll(self): print("NO.%s SchoolMember [%s] is enrolled!" % (SchoolMember.member_nums,self.name)) SchoolMember.member_nums += 1 #統計人員數量不可寫為self.member_nums += 1,因self實例下沒有此變量,兩者毫無關聯。 def tell(self): print("Hello, My name is [%s]" % self.name) class Teacher(SchoolMember): #參數為繼承對象 def __init__(self,name,age,sex,course,salary): #如果不寫__init__默認就繼承了父類的所有屬性,但子類需要額外擴展,需先重寫再繼承 super(Teacher,self).__init__(name,age,sex) #上面進行了重寫,再繼承回來進行覆蓋,推薦新式類語法 #SchoolMember.__init__(self,name,age,sex) #此為經典類語法,不推薦 self.course = course self.salary = salary def teaching(self): print("Teacher [%s] is teaching [%s]" % (self.name,self.course)) class Student(SchoolMember): def __init__(self,name,age,sex,course,tuition,): super(Student,self).__init__(name,age,sex) self.course = course self.tuition = tuition def pay_tuition(self): print("Student [%s] pays tuition [%s] again" % (self.name,self.tuition)) #實例化老師 t1 = Teacher('Stanley',24,'M','Python',1000) #實例化,不可實例化SchoolMember,其只可用來繼承 t1 = Teacher('Katrina',29,'M','MySQL',1500) #實例化,不可實例化SchoolMember,其只可用來繼承 #實例化學生 s1 = Student('Tim',24,'F','MySQL',10000) s2 = Student('Bella',27,'M','Python',12000) print("===========子類使用父類方法============") t1.tell() s1.tell() print("===========子類使用私有方法============") t1.teaching() s1.pay_tuition() Result: NO.0 SchoolMember [Stanley] is enrolled! NO.1 SchoolMember [Katrina] is enrolled! NO.2 SchoolMember [Tim] is enrolled! NO.3 SchoolMember [Bella] is enrolled! ===========子類使用父類方法============ Hello, My name is [Katrina] Hello, My name is [Tim] ===========子類使用私有方法============ Teacher [Katrina] is teaching [MySQL] Student [Tim] pays tuition [10000] again
此為標准繼承示例,其中包含三個類,SchoolMember是父類,Teacher和Student是子類,子類中擁有父類的共同屬性,除此之外,子類還需要額外進行擴展,所以在子類中要先重寫,再從父類繼承回來進行覆蓋。在繼承語法中super為關鍵字,不可省略,其參數為子類名稱,self,__init__參數為需要繼承的屬性。相關封裝特性詳見上一段落,不再累述。父類和子類定義好後,實例化Teacher和 Student,在父類中執行注冊方法,每個子類都可調用父類的tell方法,並且每個子類都會包含私有方法teaching和pay_tuition,這就利用繼承特性實現了簡單的權限限制功能。
綜上所述,對於面向對象的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。
多態性(polymorphisn)是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
Pyhon不支持多態並且也用不到多態,多態的概念是應用於Java和C#這一類強類型語言中的。那麼,多態的作用是什麼呢?我們知道,封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是為了——代碼重用。而多態則是為了實現另一個目的——接口重用!多態的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。
通過Python模擬的多態:
class Animal: def __init__(self, name): # Constructor of the class self.name = name def talk(self): # Abstract method, defined by convention only raise NotImplementedError("Subclass must implement abstract method") class Cat(Animal): def talk(self): return 'Meow!' class Dog(Animal): def talk(self): return 'Woof! Woof!' animals = [Cat('Missy'), Dog('Lassie')] for animal in animals: print animal.name + ': ' + animal.talk()