一文看懂Python的面向物件程式設計
之前在網路上看了很多關於面向物件的程式設計詳解,還是不夠過癮,所以決定自己動手寫一篇。
面向物件:Object Oriented Programming,簡稱OOP,即面向物件程式設計。
類(Class)和物件(Object)
類是用來描述具有相同屬性和方法物件的集合。物件是類的具體例項。
比如,學生都有姓名和分數,那麼這個姓名和分數就是共同的屬性,這時就可以設計一個類,用來記錄學生的姓名和成績。
這裡解釋一下屬性和方法
- 屬性:Attribute,用來描述所有物件公有的屬性,如學生的姓名和分數。
- 方法:Method,包含在類裡面的函式,也叫類函式,區別於類之外的函式,用來實現某些功能,比如打印出學生的姓名和分數。
使用關鍵詞class來建立一個類
class Student(): def __init__(self,name,score): self.name = name self.score = score def out(self): print("%s:%s"%(self.name,self.score))
以上案例中,只是定義了一個類,電腦並沒有建立儲存空間。
只有完成類的例項化,才能創建出類的具體的物件,併為之分配儲存空間。所以說,物件是類的一個例項。
接下來建立一個物件,只需要新增一下兩行程式碼即可實現
Student1 = Student('Anny','100') Student2 = Student('Mike','90')
這樣一來,Student是類,student1和student2是建立的這個類的具體的物件。當有以上程式碼的時候,Python會自動呼叫__init__初始自構函式來建立具體的物件。關鍵字self是非常重要的引數,代表建立了函式本身。
當建立了具體的物件之後,就可以使用Student1.name和Student1.score來分別獲取該學生的姓名和分數,也可以直接呼叫方法Student1.out()來獲取所有信息。
類變數與例項變數
假設現在需要新增一個計數器,每當新增一個學生時計數器就加1。
這個計數器不屬於某一個學生,而是屬於類的屬性,所以稱之為類。
而姓名和分數是屬於每個學生的,所以稱之為例項變數,也叫物件變數。
正常情況下,這樣新增一個計數器
class Student(): number = 0 def __init__(self,name,score): self.name = name self.score = score number = number + 1 def show(self): print("%s:%s"%(self.name,self.score)) student1 = Student('Anny',100) student2 = Student('Mike',90) print(student1.name)
這裡的number是類變數,所以將其放置方法外邊,name和score是例項變數,所以將其放置方法裡面。
類變數和例項變數區別很大,訪問方式也不一樣。
- 類變數:class variables,類變數在整個例項化的物件中是公用的,類變數定義在類中,且在函式體外。訪問或者呼叫類變數的具體方法是類名.變數名,或者self.__class__.變數名,self.__class__.自動返回每個物件的類名。
- 例項變數:instance variables,定義在函式之內的變數,屬於某個具體的物件,訪問或者呼叫例項變數的方法是物件名.變數名,或者self.變數名。
執行上述程式碼會發現報錯
UnboundLocalError: local variable 'number' referenced before assignment
英語賊差,某道雲翻譯的:大致意思是區域性變數number的引用之前的任務
所以說,如果要呼叫屬於類的變數number,則使用Student.number或者self.__class__.number
修改如下
class Student(): number = 0 def __init__(self,name,score): self.name = name self.score = score Student.number = Student.number + 1 def show(self): print("%s:%s"%(self.name,self.score)) student1 = Student('Anny',100) student2 = Student('Mike',90) student1.show() print(student2.number)
類方法
有些變數只屬於類,有些方法也只屬於類,不屬於具體的物件。不難發現,在屬於物件的方法裡面都有self的引數,比如__init__(self)、show(self)等,而在類中,則使用cls,與self類似,它表示類本身,一般加上@classmethod的修飾符以作說明。
這裡使用自定義的類方法來列印學生的數量
class Student(): number = 0 def __init__(self,name,score): self.name = name self.score = score Student.number = Student.number + 1 def show(self): print("%s:%s"%(self.name,self.score)) @classmethod def people(cls): print("一共有%s名學生"%Student.number) student1 = Student('Anny',100) student2 = Student('Mike',90) student1.show() student2.show() Student.people()
類的私有屬性和私有方法
類裡面的私有屬性和私有方法都是以雙下劃線__開頭的,私有屬性和方法不能在類的外部直接使用或訪問。將score變為私有屬性,然後print(Student.score),就會發現報錯。但是呼叫show的時候不會報錯,這是因為show是類裡面的函式,所以可以訪問私有變數。
私有方法也是同樣的道理,值得注意的是,私有方法必須含有self引數並且將其作為第一個引數。
在面向物件的程式設計中,通常很少讓外部類直接訪問類內部的屬性和方法,而是向外部提供一些按鈕,對其內部的成員進行訪問,以保證程式的安全性,這就叫封裝。
@property def scores(self): print("該學生成績為%s"%self.score)
加上裝飾器之後不用加括號可直接呼叫。
類的繼承
面向物件程式設計最大的好處就是避免重複的程式碼,也就是將一段程式碼重複使用,方法之一就是繼承。
先定義一個基類或者父類,再通過class 類名(父類):pass 建立子類,這樣一來,子類獲得了父類的所有屬性和方法,這種現象就叫做繼承。
再寫段程式碼,用Schoolmember表示父類,姓名和年齡是所有人的屬性,然而老師有工資(salary)這個專有屬性,學生有分數(score)這個專有屬性
# 建立父類學校成員SchoolMember class SchoolMember: def __init__(self, name, age): self.name = name self.age = age def tell(self): # 列印個人資訊 print('Name:"{}" Age:"{}"'.format(self.name, self.age), end=" ") # 建立子類老師 Teacher class Teacher(SchoolMember): def __init__(self, name, age, salary): SchoolMember.__init__(self, name, age) # 利用父類進行初始化 self.salary = salary # 方法重寫 def tell(self): SchoolMember.tell(self) print('Salary: {}'.format(self.salary)) # 建立子類學生Student class Student(SchoolMember): def __init__(self, name, age, score): SchoolMember.__init__(self, name, age) self.score = score def tell(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: $60000 student1.tell()# Name:"Mary" Age:"12" score: 99
通過以上程式碼不難看出
- 在建立子類的過程中,需要手動呼叫父類的建構函式__init__來完成子類的建立。
- 在子類中呼叫父類的方法時,需加上父類的類名字首,且必須帶self引數變數。例SchoolMember.tell(self)。
- 如果子類呼叫了每個方法或者屬性,Python會先在父類中尋找,找不到就會去子類尋找。
在實際的專案中,一個子類可以繼承多個父類。
使用super()關鍵字呼叫父類
在子類中可以使用super關鍵字直接呼叫父類中的屬性或者方法,簡化程式碼,也反映出人生苦短,我用Python的宗旨。
# 建立子類學生Student class Student(SchoolMember): def __init__(self, name, age, score): SchoolMember.__init__(self, name, age) self.score = score def tell(self): super().tell() # 等同於 SchoolMember.tell(self) print('score: {}'.format(self.score))
以上的例子中,學生子類呼叫了父類的tell方法,等同於SchoolMember.tell(self),使用super關鍵字呼叫時,需去掉括號裡的self。