面向對象程序設計(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.age
if __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 c
if __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修飾的方法。靜態方法既可以直接通過類名調用,也可以通過類的實例對象調用。
與類方法不同的地方在於,靜態方法中沒有類方法中的第一個類參數。
@staticmethod
def 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=sno
if __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 datetime
class 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.birthyear
if __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屬性賦值爲105
print('年齡:%d,成績:%d'%(stu.age,stu.score))
注意:在類的setter和getter方法中使用self訪問屬性時,需要在屬性名前加上下劃線,否則系統會因不斷遞歸調用而報錯。
end