python語言的核心內容與類與對象有關。類與對象各自的內容也有著不同。類是用來描述具有相同屬性和方法對象的集合。而對象又是類里面一個具體的東西。那么這樣講起來是不就是就沒有那么難以理解了。類方法與類對象的內容也需要我們多多了解。
幫你理解Python面向對象的編程的基本概念和核心思想。本文內含很多實例代碼,以幫助新手更好理解。如果你在學習基于Python的Django Web開發框架,因為Django編程就是采用面向對象的編程。
類(Class)與對象(Object)
類(Class)是用來描述具有相同屬性(Attribute)和方法(Method)對象的集合。對象(Object)是類(Class)的具體實例。比如學生都有名字和分數,他們有著共同的屬性。這時我們就可以設計一個學生類, 用于記錄學生的名字和分數,并自定義方法打印出他們的名字和方法。屬性(Attribute): 類里面用于描述所有對象共同特征的變量或數據。比如學生的名字和分數。
方法(Method): 類里面的函數,用來區別類外面的函數, 用來實現某些功能。比如打印出學生的名字和分數。
要創建一個類我們需要使用關鍵詞class. 這個學生類Student看上去應該是這樣的:# 創建一個學生類classStudent: # 定義學生屬性,初始化方法def__init__(self,name,score): self.name = name self.score = score # 定義打印學生信息的方法defshow(self): print("Name: {}. Score: {}".format(self.name,self.score))
在這個案例中,我們只定義了一個抽象的類,電腦并沒有創建什么存儲空間。只有當我們完成類的實例化(Instance)時,電腦才會創建一個具體的對象(Object),并為之分配存儲空間。所以對象(Object)是類(Class)的一個實例。
要創建一個具體的學生對象(Object),我們還需要輸入:student1 = Student("John",100)student2 = Student("Lucy",99)
在這個案例中,Student是類,student1和student2是我們創建的具體的學生對象。當我們輸入上述代碼時,Python會自動調用默認的__init__初始構造函數來生成具體的對象。關鍵字self是個非常重要的參數,代表創建的對象本身。
當你創建具體的對象后,你可以直接通過student1.name和student1.score來分別獲取學生的名字和分數,也可以通過student1.show()來直接打印學生的名字和分數。
類變量(class variables)與實例變量(instance variables)
假設我們需要在Student類里增加一個計數器number,每當一個新的學生對象(Object)被創建時,這個計數器就自動加1.由于這個計數器不屬于某個具體學生,而屬于Student類的,所以被稱為類變量(class variables)。而姓名和分數屬于每個學生對象的,所以屬于實例變量(instance variables),也被稱為對象變量(object variables)。
這個新Student類看上去應該是這樣的:# 創建一個學生類classStudent: # number屬于類變量,定義在方法外,不屬于具體實例number = 0# 定義學生屬性,初始化方法 # name和score屬于實例變量,定義在方法里def__init__(self,name,score): self.name = name self.score = score # 此處有錯誤 number= number + 1# 定義打印學生信息的方法defshow(self): print("Name: {}. Score: {}".format(self.name,self.score))
類變量和實例變量的區別很大,訪問方式也也不一樣。類變量:類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。訪問或調用類變量的正確方式是類名.變量名或者self.__class__.變量名。self.__class__自動返回每個對象的類名。
實例變量:定義在方法中的變量,屬于某個具體的對象。訪問或調用實例變量的正確方式是對象名.變量名或者self.變量名.
注意到上述Student類有個錯誤沒? 我們試圖直接使用number = number + 1調用屬于類的變量number。正確的方式是使用Student.number或self.__class__.number訪問屬于類的變量。下面的代碼才是正確的:# 創建一個學生類classStudent: # number屬于類變量,不屬于某個具體的學生實例number = 0# 定義學生屬性,初始化方法 # name和score屬于實例變量def__init__(self,name,score): self.name = name self.score = scoreStudent.number = Student.number + 1# 定義打印學生信息的方法defshow(self): print("Name: {}. Score: {}".format(self.name,self.score))# 實例化,創建對象student1 = Student("John",100)student2 = Student("Lucy",99)print(Student.number) # 打印2print(student1.__class__.number) # 打印2
類方法(Class method)
正如同有些變量只屬于類,有些方法也只屬于類,不屬于具體的對象。你有沒有注意到屬于對象的方法里面都有一個self參數, 比如__init__(self), show(self)?self是指對象本身。屬于類的方法不使用self參數, 而使用參數cls,代表類本身。另外習慣上對類方法我們會加上@classmethod的修飾符做說明。
同樣拿Student為例子,我們不用print函數打印出已創建學生對象的數量,而是自定義一個類方法來打印,我們可以這么做:classStudent: # number屬于類變量,不屬于某個具體的學生實例number = 0# 定義學生屬性,初始化方法 # name和score屬于實例變量def__init__(self,name,score): self.name = name self.score = score Student.number = Student.number + 1# 定義打印學生信息的方法defshow(self): print("Name: {}. Score: {}".format(self.name,self.score)) # 定義類方法,打印學生的數量@classmethoddeftotal(cls): print("Total: {0}".format(cls.number))# 實例化,創建對象student1 = Student("John",100)student2 = Student("Lucy",99)Student.total() # 打印 Total: 2
類的私有屬性(private attribute)和私有方法(private method)
類里面的私有屬性和私有方法以雙下劃線__開頭。私有屬性或方法不能在類的外部被使用或直接訪問。我們同樣看看學生類這個例子,把分數score變為私有屬性,看看會發生什么。# 創建一個學生類classStudent: # 定義學生屬性,初始化方法 # name和score屬于實例變量, 其中__score屬于私有變量def__init__(self,name,score): self.name = name self.__score = score # 定義打印學生信息的方法defshow(self): print("Name: {}. Score: {}".format(self.name,self.__score))# 實例化,創建對象student1 = Student("John",100)student1.show() # 打印 Name: John, Score: 100student1.__score # 打印出錯,該屬性不能從外部訪問。
如果你將score變成__score, 你將不能直接通過student1.__score獲取該學生的分數。show()可以正常顯示分數,是因為它是類里面的函數,可以訪問私有變量。
私有方法是同樣的道理。當我們把show()變成,__show()你將不能再通過student1.__show()打印出學生的名字和分數。值得注意的是私有方法必需含有self這個參數,且把它作為第一個參數。
在面向對象的編程中,通常情況下很少讓外部類直接訪問類內部的屬性和方法,而是向外部類提供一些按鈕,對其內部的成員進行訪問,以保證程序的安全性,這就是封裝。
@property的用法與神奇之處
在上述案例中用戶不能用student1.__score方式訪問學生分數,然而用戶也就知道了__score是個私有變量。我們有沒有一種方法讓用戶通過student1.score來訪問學生分數而繼續保持__score私有變量的屬性呢?這時我們就可以借助python的@property裝飾器了。我們可以先定義一個方法score(), 然后利用@property把這個函數偽裝成屬性。見下面例子:# 創建一個學生類classStudent: # 定義學生屬性,初始化方法 # name和score屬于實例變量, 其中score屬于私有變量def__init__(self,name,score): self.name = name self.__score = score # 利用property裝飾器把函數偽裝成屬性@propertydefscore(self): print("Name: {}. Score: {}".format(self.name,self.__score))# 實例化,創建對象student1 = Student("John",100)student1.score # 打印 Name: John. Score: 100
注意:一旦給函數加上一個裝飾器@property,調用函數的時候不用加括號就可以直接調用函數了
類的繼承(Inheritance)
面向對象的編程帶來的最大好處之一就是代碼的重用,實現這種重用的方法之一是通過繼承(Inheritance)。你可以先定義一個基類(Base class)或父類(Parent class),再按通過class 子類名(父類名)來創建子類(Child class)。這樣子類就可以從父類那里獲得其已有的屬性與方法,這種現象叫做類的繼承。
我們再看另一個例子,老師和學生同屬學校成員,都有姓名和年齡的屬性,然而老師有工資這個專有屬性,學生有分數這個專有屬性。這時我們就可以定義1一個學校成員父類,2個子類。# 創建父類學校成員SchoolMemberclassSchoolMember: def__init__(self,name,age): self.name = name self.age = age deftell(self): # 打印個人信息print('Name:"{}" Age:"{}"'.format(self.name,self.age),end=" ")# 創建子類老師 TeacherclassTeacher(SchoolMember): def__init__(self,name,age,salary): SchoolMember.__init__(self,name,age) # 利用父類進行初始化self.salary = salary # 方法重寫deftell(self): SchoolMember.tell(self) print('Salary: {}'.format(self.salary))# 創建子類學生StudentclassStudent(SchoolMember): def__init__(self,name,age,score): SchoolMember.__init__(self,name,age) self.score = score deftell(self): SchoolMember.tell(self) print('score: {}'.format(self.score))teacher1 = Teacher("John",44."$60000")student1 = Student("Mary",12.99)teacher1.tell() # 打印 Name:"John" Age:"44" Salary: $60000student1.tell() # Name:"Mary" Age:"12" score: 99
上述代碼中,你注意到以下幾點了嗎?在創建子類的過程中,你需要手動調用父類的構造函數__init__來完成子類的構造。
在子類中調用父類的方法時,需要加上父類的類名前綴,且需要帶上self參數變量。比如SchoolMember.tell(self), 這個可以通過使用super關鍵詞簡化代碼。
如果子類調用了某個方法(如tell())或屬性,Python會先在子類中找,如果找到了會直接調用。如果找不到才會去父類找。這為方法重寫帶來了便利。
實際Python編程過程中,一個子類可以繼承多個父類,原理是一樣的。第一步總是要手動調用__init__構造函數。
super()關鍵字調用父類方法
在子類當中可以通過使用super關鍵字來直接調用父類的中相應的方法,簡化代碼。在下面例子中,學生子類調用了父類的tell()方法。super().tell()等同于SchoolMember.tell(self)。當你使用Python super()關鍵字調用父類方法時時,注意去掉括號里self這個參數。# 創建子類學生StudentclassStudent(SchoolMember): def__init__(self,name,age,score): SchoolMember.__init__(self,name,age) self.score = score deftell(self): super().tell() # 等同于 SchoolMember.tell(self)print('score: {}'.format(self.score))
通過一些舉例來介紹這些具體內容理解起來就不會那么空洞。在現有的學識上,我們還需要不斷進步才能趕上時代發展的步伐。Python語言的學習是需要不斷提煉的一個過程。因此,掌握好學習方法的同時還需要自己去領悟一些書本里面學不到的東西。想要了解Python語言的更多信息,請繼續關注中培偉業。