帶你掌握Python面向對象方法,與C++面向對象編程到底有啥區別?

面向對象程序設計(Object Oriented Programming)作爲一種新方法,其本質是以建立模型體現出來的抽象思維過程和麪向對象的方法。模型是用來反映現實世界中事物特徵的。

 

類和對象是面向對象程序設計的兩個重要概念。

類和對象的關係即數據類型與變量的關係,根據一個類可以創建多個對象,而每個對象只能是某一個類的對象。類規定了可以用於存儲什麼數據,而對象用於實際存儲數據,每個對象可存儲不同的數據。

與C/C++等語言不同,Python中提供的基本數據類型也是類,如int、float等。

 

類中可以包含各種屬性及各種方法。屬性對應一個類可以用來保存哪些數據,而方法對應一個類可以支持哪些操作(即數據處理)。

 

通過類,可以把數據和操作封裝在一起,從而使得程序結構更加清晰,這也就是所謂的類的封裝性。例:定義一個空類

class Student: #定義一個名字爲Student的類    pass #一個空語句,起到佔位作用,表示Student類中         #沒有任何屬性和方法if __name__=='__main__':    stu=Student() #創建Student類的對象,並將創建的對象賦給變量stu    print(stu) #輸出stu   #  輸出  <__main__.Student object at 0x00000216EE7DF0F0>#  提示:每次創建對象時,系統都會在內存中選擇一塊區域分配給對象,每次# 選擇的內存通常是不一樣的。因此,實際運行時會看到一個不同的stu對象地址。

對類屬性的訪問,既可以直接通過類名訪問,也可以通過該類的對象

訪問,訪問方式爲:

類名或對象名.屬性名

 

私有屬性,是指在類內可以直接訪問、而在類外無法直接訪問的屬性。

Python中規定,在定義類時,如果一個類屬性名是以__(兩個下劃線)開頭,則該類屬性爲私有屬性。

實際上,Python中並不存在無法訪問的私有屬性。如果我們在類中定

義了一個私有屬性,則在類外訪問該私有屬性時需要在私有屬性名

前加上“_類名”。例如:

class Student: #定義Student類   __id='未知' #定義Student類中有一個__id私有屬性
if __name__=='__main__':  stu=Student() #定義Student類對象stu  print('身份證號:%s'%stu._Student__id)

類中的方法分爲兩類:普通方法和內置方法。

• 普通方法需要通過類的實例對象根據方法名調用;

• 內置方法是在特定情況下由系統自動執行。

 

在定義類的普通方法時,要求第一個參數需要對應調用方法時所使用的實例對象(一般命名爲self,但也可以改爲其他名字)。在通過類的實例對象調用類中的普通方法時,並不需要傳入self參數的值,self會自動對應調用該方法時所使用的對象。

 

注意:通過實例對象調用時會自動將該實例對象傳給self,而通過類調用時則不會有這個隱含的參數傳遞。

 

構造方法是Python類中的內置方法之一,它的方法名爲__init__,在創建一個類對象時會自動執行,負責完成新創建對象的初始化工作。

 

析構方法是類的另一個內置方法,它的方法名爲__del__,在銷燬一個類對象時會自動執行,負責完成待銷燬對象的資源清理工作,類對象銷燬有如下三種情況:

(1)局部變量的作用域結束。

(2)使用del刪除對象。

(3)程序結束時,程序中的所有對象都將被銷燬。

常見的內置方法

 

__str__ 函數  :str函數對類對象進行處理時或者調用Python內置函數format()和print()時自動執行,__str__方法的返回值必須是字符串。

比較運算的內置方法

 

__gt__(self, other) 進行self>other運算時自動執行

__lt__(self, other) 進行self<other運算時自動執行

__ge__(self, other) 進行self>=other運算時自動執行

__le__(self, other) 進行self<=other運算時自動執行

__eq__(self, other) 進行self==other運算時自動執行

__ne__(self, other) 進行self!=other運算時自動執行

 

class Student: #定義Student類    def __init__(self, name, age): #定義構造方法        self.name=name #將self對應對象的name屬性賦爲形參        #name的值        self.age=age #將self對應對象的age屬性賦爲形參age的值    def __le__(self, other): #定義內置方法__le__        return self.age<=other.ageif __name__=='__main__’:    stu1=Student('李曉明',19) # 定義Student類對象stu1    stu2=Student('馬紅',20) #定義Student類對象stu2    print('馬紅的年齡小於等於李曉明的年齡:', stu2<=stu1) #輸出 馬紅的年齡小於等於李曉明的年齡:False

 

內置函數isinstance 用於判斷一個對象所屬的類是否是指定類或指定類的子類;

內置函數issubclass 用於判斷一個類是否是另一個類的子類;

內置函數type  用於獲取一個對象所屬的類。

 

如果我們要判斷一個對象的類型是否是指定類或該類的子類,則可以使用isinstance函數

如果我們要判斷一個對象的類型是否是指定類,則可以使用“type(對象名)==類名”的方式。

 

繼承

繼承允許開發者基於已有的類創建新的類。

-如果一個類C1通過繼承已有類C而創建,則將C1稱作子類(subclass),將C稱做基類、父類或超類(base class、super class)

-子類會繼承父類中定義的所有屬性和方法,另外也能夠在子類中增加新的屬性和方法。

-如果一個子類只有一個父類,則將這種繼承關係稱爲單繼承;如果一個子類有兩個或更多父類,則將這種繼承關係稱爲多重繼承

子類的定義 。定義子類時需要指定父類,格式爲:

class 子類名(父類名1, 父類名2, …, 父類名M):
 
​​​​​​​ 語句1    
 語句2
    …

 語句N

 

方法重寫 是指子類可以對從父類中繼承過來的方法進行重新定義,從而使得

子類對象可以表現出與父類對象不同的行爲。

class Person: #定義Person類    def __init__(self, name): #定義構造方法        self.name=name #將self對象的name屬性賦爲形參name的值    def PrintInfo(self): #定義PrintInfo方法        print('姓名:%s'%self.name)class Student(Person): #以Person類作爲父類定義子類Student    def __init__(self, sno, name): #定義構造方法        self.sno=sno #將self對象的sno屬性賦爲形參sno的值        self.name=name #將self對象的name屬性賦爲形參name的值    def PrintInfo(self): #定義PrintInfo方法        print('學號:%s,姓名:%s'%(self.sno,self.name))def PrintPersonInfo(person): #定義普通函數PrintPersonInfo    print('PrintPersonInfo函數中的輸出結果', end='#')    person.PrintInfo() #通過person調用PrintInfo方法if __name__=='__main__':    p=Person('李曉明') #創建Person類對象p    stu=Student('1810100','李曉明') #創建Student類對象stu    p.PrintInfo()    stu.PrintInfo()    PrintPersonInfo(p)    PrintPersonInfo(stu)#輸出     李曉明1810100,姓名:李曉明李曉明1810100,姓名:李曉明

多態,是指在執行同樣代碼的情況下,系統會根據對象實際所屬的類去調用相應類中的方法。

super方法

super方法用於獲取父類的代理對象,以執行已在子類中被重寫的父

類方法,其語法格式爲:

super([類名[, 對象名或類名]])

super方法有兩個參數:

第一個參數是要獲取父類代理對象的類名。

第二個參數如果傳入對象名,則該對象所屬的類必須是第一個參數指定的類或該類的子類,找到的父類對象的self會綁定到這個對象上;如果傳入類名,則該類必須是第一個參數指定的類的子類。

 

在一個類A的定義中調用super方法時,可以將兩個參數都省略,此時,super()等價於super(A, self),即獲取A的父類代理對象,且獲取到的父類代理對象中的self綁定到當前A類對象的self上。

class Person: #定義Person類  def __init__(self, name): #定義構造方法    print('Person類構造方法被調用!')    self.name=name #將self對象的name屬性賦爲形參name的值class Student(Person): #以Person類作爲父類定義子類Student  def __init__(self, sno, name): #定義構造方法    print('Student類構造方法被調用!')    super().__init__(name) #調用父類的構造方法9     self.sno=sno #將self對象的sno屬性賦爲形參sno的值class Postgraduate(Student): #以Student類作爲父類定義子類Postgraduate  def __init__(self, sno, name, tutor): #定義構造方法    print('Postgraduate類構造方法被調用!')    super().__init__(sno, name) #調用父類的構造方法    self.tutor=tutor #將self對象的tutor屬性賦爲形參tutor的值if __name__=='__main__':  pg=Postgraduate('1810100','李曉明','馬紅') #創建Postgraduate類對象pg  print('學號:%s,姓名:%s,導師:%s'%(pg.sno,pg.name,pg.tutor))
#輸出  Postgraduate類構造方法被調用!Student類構造方法被調用!Person類構造方法被調用!學號:1810100,姓名:李曉明,導師:馬紅

“super().__init__(sno,name)”與“super(Postgraduate,self).__init__(sno, name) ”等價。

類方法

類方法是指使用@classmethod修飾的方法,其第一個參數是類本身(而不是類的實例對象)。

類方法的特點是既可以通過類名直接調用,也可以通過類的實例對象調用。

class Complex: #定義Complex類   def __init__(self,real=0,image=0): #定義構造方法       self.real=real #初始化一個複數的實部值       self.image=image #初始化一個複數的虛部值   @classmethod   def add(cls,c1,c2): #定義類方法add,實現兩個複數的加法運算       print(cls) #輸出cls       c=Complex() #創建Complex類對象c       c.real=c1.real+c2.real #實部相加       c.image=c1.image+c2.image #虛部相加       return cif __name__=='__main__':   c1=Complex(1,2.5)   c2=Complex(2.2,3.1)   c=Complex.add(c1,c2) #直接使用類名調用類方法add   print('c1+c2的結果爲%.2f+%.2fi'%(c.real,c.image))#輸出   <class '__main__.Complex'>c1+c2的結果爲3.20+5.60i

“c=Complex.add(c1,c2) ”改爲“c=c1.add(c1, c2) ”或

“c=c2.add(c1, c2)”或“c=Complex().add(c1, c2) ”,程序運行後可得到相同的輸出結果,即類方法也可以使用實例對象調用。

通過“print(cls) ”輸出類方法add的第一個參數,從輸出結果中可以看到cls是Complex類。

 

靜態方法

靜態方法是指使用@staticmethod修飾的方法。靜態方法既可以直接通過類名調用,也可以通過類的實例對象調用。

與類方法不同的地方在於,靜態方法中沒有類方法中的第一個類參數。

@staticmethoddef add(c1,c2): #定義類方法add,實現兩個複數的加法運算   c=Complex() #創建Complex類對象c   c.real=c1.real+c2.real #實部相加   c.image=c1.image+c2.image #虛部相加   return c

 

動態擴展類

Python作爲一種動態語言,除了可以在定義類時定義屬性和方法外,還可以動態地爲已經創建的對象綁定新的屬性和方法。

在給對象綁定方法時,需要使用types模塊中的MethodType方法,其第一個參數是要綁定的函數名,第二個參數是綁定的對象名。

​​​​​​​

from types import MethodType #從types模塊中導入MethodType方法 class Student: #定義學生類     pass def SetName(self,name): #定義SetName函數     self.name=name def SetSno(self,sno): #定義SetSno函數     self.sno=snoif __name__=='__main__':    stu1=Student() #定義Student類對象stu1    stu2=Student() #定義Student類對象stu2    stu1.SetName=MethodType(SetName,stu1)    #爲stu1對象綁SetName方法    Student.SetSno=SetSno #爲Student類綁定SetSno方法    stu1.SetName('李曉明')    stu1.SetSno('1810100')    #stu2.SetName('張剛') #取消註釋則會報錯,因爲職位stu1綁定了方法    stu2.SetSno('1810101')

 

__slots__函數:在定義類時,Python提供了__slots__變量以限制可動態擴展的屬性。該限制只對__slots__所在類的實例對象有效。如果子類中沒有__slots__定義,則子類的實例對象可以進行任意屬性的動態擴展。

(一般也用不到,作爲了解)

@property裝飾器

類中的屬性可以直接訪問和賦值,這爲類的使用者提供了方便,但也帶來了問題:類的使用者可能會給一個屬性賦上超出有效範圍的值。

爲了解決這個問題,Python提供了@property裝飾器,可以將類中屬性的訪問和賦值操作自動轉爲方法調用,這樣可以在方法中對屬性值的取值範圍做一些條件限定。

 

直接使用@property就可以定義一個用於獲取屬性值的方法(即getter)。如果要定義一個設置屬性值的方法(setter),則需要使用名字“@屬性名.setter”的裝飾器。

如果一個屬性只有用於獲取屬性值的getter方法,而沒有用於設置屬性值的setter方法,則該屬性是一個只讀屬性,只允許讀取該屬性的值、而不能設置該屬性的值。​​​​​​​

import datetimeclass Student: #定義Student類    @property    def score(self): #用@property裝飾器定義一個用於獲取score值的方法        return self._score    @score.setter    def score(self, score): #用score.setter定義一個用於設置score值的方法        if score<0 or score>100: #不符合0~100的限定條件            print('成績必須在0~100之間!')        else:            self._score=score    @property    def age(self): #用@property裝飾器定義一個用於獲取age值的方法        return datetime.datetime.now().year-self.birthyearif __name__=='__main__':    stu=Student() #創建Student類對象stu    stu.score=80 #將stu對象的score屬性賦值爲80    stu.birthyear=2000 #將stu對象的birthyear屬性賦值爲2000    print('年齡:%d,成績:%d'%(stu.age,stu.score))
#輸出年齡:18,成績:80#stu.age=19 #取消前面的註釋符則會報錯stu.score=105 #將stu對象的score屬性賦值爲105print('年齡:%d,成績:%d'%(stu.age,stu.score))

注意:在類的setter和getter方法中使用self訪問屬性時,需要在屬性名前加上下劃線,否則系統會因不斷遞歸調用而報錯。

 

end

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章