Python編程基礎(chǔ)與應(yīng)用第8章 類與對象_第1頁
Python編程基礎(chǔ)與應(yīng)用第8章 類與對象_第2頁
Python編程基礎(chǔ)與應(yīng)用第8章 類與對象_第3頁
Python編程基礎(chǔ)與應(yīng)用第8章 類與對象_第4頁
Python編程基礎(chǔ)與應(yīng)用第8章 類與對象_第5頁
已閱讀5頁,還剩82頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

8

類 與 對 象XXXX

大學(xué)XX

學(xué)院XXX

教授2學(xué)習(xí)目標(biāo):掌握定義類和創(chuàng)建對象的方法熟悉對象的屬性和方法熟悉類的封裝、繼承和多態(tài)掌握面向?qū)ο缶幊谭椒ㄋ颊?nèi)涵:面向?qū)ο蟪绦虻墓δ苁峭ㄟ^各種對象之間的協(xié)同作用實(shí)現(xiàn)的。廣大學(xué)子應(yīng)通過程序?qū)ο蟮南嗷プ饔皿w悟工作和學(xué)習(xí)中的合作意識,樹立團(tuán)結(jié)協(xié)作的精神。第

8

類與對象38.1 類的定義和對象創(chuàng)建8.1.1

類的定義現(xiàn)實(shí)生活中,人們通常會對具有相似特征或行為的事物進(jìn)行命名以便區(qū)別于其他事物。同理,程序中的類也有一個(gè)名稱,也包含描述類特征的數(shù)據(jù)成員及描述類行為的成員函數(shù),其中,數(shù)據(jù)成員稱為屬性,成員函數(shù)稱為方法。Python

使用class

關(guān)鍵字來定義一個(gè)類。類的語法格式如下。class

類名:屬性名

=

屬性值def

方法名(self):方法體其中,class

關(guān)鍵字標(biāo)識類的開始;類名代表類的標(biāo)識符,使用大駝峰命名法,首字母一般為大寫字母;冒號是必不可少的;冒號之后是屬性和方法,屬4性類似于前面章節(jié)中所介紹的變量,方法類似于前面章節(jié)中所介紹的函數(shù),但方法參數(shù)列表中的第

1

個(gè)參數(shù)是一個(gè)指代對象的默認(rèn)參數(shù)self。下面定義一個(gè)表示轎車的Car

類,該類中包含描述轎車顏色的屬性color和描述轎車行駛行為的方法drive(),示例代碼如程序段P8.1

所示。P8.1

自定義類class

Car:color="red"def

drive(self):print("行駛")在定義函數(shù)時(shí)只檢測語法,不執(zhí)行代碼,但是在定義類時(shí),類體代碼會在類定義階段就立刻執(zhí)行,并且會產(chǎn)生一個(gè)類的名稱空間。也就是說,類的本身其實(shí)就是一個(gè)容器(名稱空間),用來存放名字,這是類的用途之一。第

8

類與對象58.1.2 對象創(chuàng)建與使用調(diào)用類即可產(chǎn)生對象,調(diào)用類的過程又稱為類的對象化,對象化的結(jié)果稱為類的對象。創(chuàng)建對象的語法格式如下。對象名

=

類名()根據(jù)前面定義的Car

類創(chuàng)建一個(gè)對象,代碼如下。car=Car() # car

是指向調(diào)用Car()創(chuàng)建的對象的變量對象的使用本質(zhì)上就是對類或?qū)ο蟪蓡T的使用,即訪問屬性或調(diào)用方法。訪問屬性和調(diào)用方法的語法格式如下。對象名.屬性名對象名.方法名()6例如,使用car

對象訪問color

屬性,并調(diào)用drive()方法,代碼如下。print(car.color)car.drive()運(yùn)行代碼,輸出結(jié)果如下。red行駛

8.2 屬性8.2.1

類屬性與對象屬性類的成員包括屬性和方法,默認(rèn)它們可以在類的外部被訪問或調(diào)用,但考慮到數(shù)據(jù)安全問題,有時(shí)需要將其設(shè)置為私有成員,限制類外部對其進(jìn)行訪問或調(diào)用。第

8

類與對象7屬性按聲明的方式可以分為兩類:類屬性和對象屬性。下面結(jié)合對象分別介紹類屬性和對象屬性。1.

類屬性類屬性是聲明在類內(nèi)部、方法外部的屬性。例如,示例中Car

類內(nèi)部聲明的color

屬性就是一個(gè)類屬性。類屬性可以通過類對象進(jìn)行訪問,但只能通過類進(jìn)行修改。例如,定義一個(gè)只包含類屬性的Car

類,創(chuàng)建Car

類的對象,并通過類和對象分別訪問、修改類屬性,示例代碼如程序段P8.2

所示。P8.2

定義類屬性class

Car:color="red"car=Car()8#

用類名修改屬性值#

用對象名修改屬性值print(Car.color)print(car.color)Car.color='white'print(Car.color)print(car.color)car.color='black'print(Car.color)print(car.color)運(yùn)行代碼,輸出結(jié)果如下。redredwhite第

8

類與對象9whitewhiteblack分析輸出結(jié)果中的前兩個(gè)數(shù)據(jù)可知,Car

類和car

對象成功地訪問了類屬性,結(jié)果都為“red”;分析中間的兩個(gè)數(shù)據(jù)可知,Car

類成功地修改了類屬性的值,因此Car

類和car

對象訪問的結(jié)果為“white”;分析最后兩個(gè)數(shù)據(jù)可知,Car類訪問的類屬性的值仍然是“white”,而

car

對象訪問的結(jié)果為“black”,說明car

對象不能修改類屬性的值。為什么通過

car

對象最后一次訪問類屬性的值為“

black

”?

因?yàn)椤癱ar.color='black'”語句起到了添加一個(gè)與類屬性同名的對象屬性的作用。2.

對象屬性對象屬性是在方法內(nèi)部聲明的屬性,Python

支持動態(tài)添加對象屬性。下面10從訪問對象屬性、修改對象屬性和動態(tài)添加對象屬性

3

個(gè)方面對對象屬性進(jìn)行介紹。1)

訪問對象屬性對象屬性只能通過對象進(jìn)行訪問。例如,定義一個(gè)包含方法和對象屬性的類Car,創(chuàng)建Car

類的對象,并訪問對象屬性,示例代碼如程序段P8.3

所示。P8.3

定義對象屬性class

Car:def

drive(self):self.color='white'car=Car()car.drive()print(car.color)第

8

類與對象11運(yùn)行代碼,輸出結(jié)果如下。white以上代碼在drive()方法中使用self

關(guān)鍵字定義了一個(gè)顏色屬性;通過對象car

成功地訪問了對象屬性。在代碼段P8.3

后面添加以下代碼。print(Car.color)運(yùn)行代碼,輸出結(jié)果如下。whiteTraceback(mostrecentcall

last):File"D:/w1.py",line8,in

<module>print(Car.color)AttributeError:

type

object

'Car'

has

no

attribute

'color'由運(yùn)行結(jié)果可知,程序通過對象

car

成功地訪問了對象屬性,當(dāng)通過類

Car12訪問對象屬性時(shí)出現(xiàn)了錯誤,說明對象屬性只能通過對象訪問,不能通過類訪問。2)

修改對象屬性對象屬性通過對象進(jìn)行修改。例如,在以上示例中修改對象屬性的代碼如程序段P8.4

所示。P8.4

修改對象屬性class

Car:def

drive(self):self.color='white'car=Car()car.drive()print(car.color)car.color='red'第

8

類與對象13print(car.color)運(yùn)行代碼,輸出結(jié)果如下。whitered3)

動態(tài)添加對象屬性Python

支持在類的外部使用對象動態(tài)地添加對象屬性。例如,在以上示例的末尾動態(tài)添加對象屬性wheels,示例代碼如程序段P8.5

所示。P8.5

添加對象屬性class

Car:def

drive(self):self.color='white'car=Car()14car.drive()print(car.color)car.wheels=4print(car.wheels)運(yùn)行代碼,輸出結(jié)果如下。white4程序成功地添加了對象屬性,并通過對象訪問了新增加的對象屬性。8.2.2

公有屬性與私有屬性Python

語言類的屬性的可見度有兩種:公有屬性和私有屬性。默認(rèn)是公有屬性,可以在類的外部通過類或?qū)ο箅S意訪問。使用雙下畫線“

”開頭的是私有屬性,僅允許類內(nèi)部訪問,類對象和派生類均不能訪問此屬性。使用單下第

8

類與對象15畫線“_”是一種約定的成員保護(hù),它表示該屬性是受保護(hù)的,只有類和子類的對象能訪問。私有屬性定義與使用的示例代碼如程序段P8.6

所示。P8.6

定義私有屬性class

Car:

color='white'car=Car()print(car.

color)運(yùn)行代碼,輸出結(jié)果如下。Traceback(mostrecentcall

last):File"D:/w1.py",line7,in

<module>print(car.

color)16AttributeError:

'Car'

object

has

no

attribute

'

color'輸出結(jié)果是異常信息,說明類的外面不能訪問類的私有屬性。如果要訪問類的私有屬性,可以通過類的公有方法來訪問。示例代碼如程序段P8.7

所示。P8.7

通過公有方法訪問私有屬性class

Car:

color='white'def

test(self):print(f"車的顏色是{self.

color}")car=Car()car.test()運(yùn)行代碼,輸出結(jié)果如下。車的顏色是white第

8

類與對象17由輸出結(jié)果可知,通過公有方法test

成功地訪問了類的私有屬性。8.2.3 特殊屬性Python

通過在屬性名稱前面和后面添加雙下畫線(

)的方式來表示特殊屬性,示例代碼如程序段P8.8

所示。P8.8

定義特殊屬性class

Car:

color

='white'car=Car()print(car.

color

)運(yùn)行代碼,輸出結(jié)果如下。white從運(yùn)行結(jié)果可知,在類的外面可以訪問特殊屬性。在編程時(shí)可以定義特殊18屬性來設(shè)計(jì)特殊功能。Python

語言已經(jīng)定義了一些特殊屬性。例如,obj.

dict

表示對象的屬性字典、obj.

class

表示對象所屬的類等。

8.3 方法對象方法、類方法、靜態(tài)方法與property

方法Python

中的方法按定義方式可以分為對象方法、類方法、靜態(tài)方法和property

方法

4

類。對象方法對象方法形似函數(shù),但它定義在類內(nèi)部,以self

為第一個(gè)形參。例如,前面聲明的drive()就是一個(gè)對象方法。對象方法中的

self

參數(shù)代表對象本身,它會在對象方法被調(diào)用時(shí)自動接收由系統(tǒng)傳遞的調(diào)用該方法的對象。對象方法只能通過對象調(diào)用。例如,定義一個(gè)包含對象方法

drive()的類Car,第

8

類與對象19創(chuàng)建Car

類的對象,分別通過對象和類調(diào)用對象方法,示例代碼如程序段P8.9所示。P8.9

定義對象方法class

Car:def

drive(self):print('這是對象方法')car=Car()car.drive()Car.drive()運(yùn)行代碼,輸出結(jié)果如下。這是對象方法Traceback(mostrecentcall

last):20File"D:/w1.py",line7,in

<module>Car.drive()TypeError:Car.drive()missing1

requiredpositionalargument:'self'從以上結(jié)果可以看出,程序通過對象成功地調(diào)用了對象方法,通過類則無法調(diào)用對象方法。2.

類方法類方法是定義在類內(nèi)部,使用裝飾器@classmethod

修飾的方法。類方法的語法格式如下。@classmethoddef

類方法名(cls):方法體類方法中參數(shù)列表的第一個(gè)參數(shù)為cls,代表類本身,它會在類方法被調(diào)用第

8

類與對象21時(shí)自動接收由系統(tǒng)傳遞的調(diào)用該方法的類。例如,定義一個(gè)包含類方法stop()的Car

類,示例代碼如程序段P8.10

所示。P8.10

定義類方法class

Car:@classmethoddef

stop(cls):print('這是類方法')car=Car()car.stop()Car.stop()運(yùn)行代碼,輸出結(jié)果如下。這是類方法 這是類方法22從以上結(jié)果可以看出,程序通過對象和類成功地調(diào)用了類方法。在類方法中,可以使用cls

訪問和修改類屬性的值。示例代碼如程序段P8.11所示。P8.11

在類方法中修改類屬性的值class

Car:color='black'@classmethoddef

stop(cls):print(cls.color)cls.color='red'print(cls.color)car=Car()car.stop()第

8

類與對象23運(yùn)行代碼,輸出結(jié)果如下。blackred從以上結(jié)果可以看出,程序在類方法stop()中成功地訪問和修改了類屬性color

的值。3.

靜態(tài)方法靜態(tài)方法是定義在類內(nèi)部,使用裝飾器@staticmethod

修飾的方法。靜態(tài)方法的語法格式如下。@staticmethoddef

靜態(tài)方法名():方法體24靜態(tài)方法沒有任何默認(rèn)參數(shù),它適用于與類無關(guān)的操作,或者無須使用類成員的操作,常見于一些工具類中。例如,定義一個(gè)包含靜態(tài)方法的Car

類,示例代碼如程序段P8.12

所示。P8.12

定義靜態(tài)方法class

Car:@staticmethoddef

test():print("這是靜態(tài)方法")car=Car()car.test()Car.test()運(yùn)行代碼,輸出結(jié)果如下。第

8

類與對象25這是靜態(tài)方法這是靜態(tài)方法由運(yùn)行結(jié)果可知,靜態(tài)方法可以通過類和對象調(diào)用。靜態(tài)方法內(nèi)部不能直接訪問屬性或方法,但可以使用類名訪問類屬性或調(diào)用類方法,示例代碼如程序段P8.13

所示。P8.13

通過類名訪問類屬性、調(diào)用類方法class

Car:color='black'@staticmethoddef

test():print("這是靜態(tài)方法")print(f"類屬性color

的值為{Car.color}")26car=Car()car.test()運(yùn)行代碼,輸出結(jié)果如下。這是靜態(tài)方法類屬性color

的值為black由運(yùn)行結(jié)果可知,在靜態(tài)方法test()中,通過類名成功地訪問了類屬性。4.

property

方法@property

裝飾器用來裝飾類的方法,通過@property

裝飾的方法,可以直接通過方法名調(diào)用方法,不需要在方法名后面添加圓括號。@property

裝飾器常用來裝飾訪問私有屬性的方法,以保護(hù)類的封裝特性。語法格式如下。@propertydef

方法名():第

8

類與對象27

方法體例如,定義一個(gè)帶有年齡私有屬性的人類,并定義一個(gè)@property

裝飾的方法訪問年齡私有屬性。示例代碼如程序段P8.14

所示。P8.14 @property

裝飾的方法class

Person:def

init

(self):self.

age=

18@propertydef

age(self):return

self.

ageperson=Person()print(person.age)28運(yùn)行代碼,輸出結(jié)果為

18。通過不帶括號的age

成功地訪問了私有屬性。@property

裝飾age()方法,使得該方法變成了age

屬性的getter()方法。但是,此時(shí)age

屬性只具有只讀屬性,不能修改其值。若要將其變成可寫屬性,則需要為age

屬性添加setter()方法,這時(shí)就要用到setter

裝飾器。示例代碼如程序段P8.15

所示。P8.15

@property

裝飾的方法變成了屬性class

Person:def

init

(self):self.

age=

18@propertydef

age(self):return

self.

age@age.setter第

8

類與對象29def

age(self,age):self.

age=

ageperson=Person()print(person.age)person.age=25print(person.age)運(yùn)行代碼,輸出結(jié)果為

18,25。通過age

屬性正確地修改了年齡私有屬性的值。8.3.2

公有方法與私有方法Python

語言類的方法的可見度有兩種:公有方法和私有方法。默認(rèn)是公有方法,可以在類的外部通過類或?qū)ο箅S意調(diào)用。使用雙下畫線“

”開頭的是私有方法,僅允許類內(nèi)部調(diào)用,類對象和派生類均不能調(diào)用此方法。30私有方法定義與使用的示例代碼如程序段P8.16

所示。P8.16

定義私有方法class

Car:def

drive(self):print("行駛")car=Car()car.

drive()運(yùn)行代碼,輸出結(jié)果如下。Traceback(mostrecentcall

last):File"D:/w1.py",line8,in

<module>car.

drive()AttributeError:

'Car'

object

has

no

attribute

'

drive'第

8

類與對象31輸出結(jié)果是異常信息,說明類的外面不能調(diào)用類的私有方法。如果要調(diào)用類的私有方法,可以通過類的公有方法來調(diào)用。示例代碼如程序段

P8.17

所示。P8.17

通過公有方法調(diào)用私有方法class

Car:def

drive(self):print("行駛")def

test(self):self.

drive()car=Car()car.test()運(yùn)行代碼,輸出結(jié)果如下。行駛32由運(yùn)行結(jié)果可知,通過公有方法test

成功地調(diào)用了類的私有方法。8.3.3 特殊方法Python

通過在方法名稱前面和后面添加雙下畫線(

)的方式來表示特殊方法,示例代碼如程序段P8.18

所示。P8.18

定義特殊方法class

Car:def

drive

(self):print("行駛")car=Car()car.

drive

()運(yùn)行代碼,輸出結(jié)果如下。行駛第

8

類與對象33從運(yùn)行結(jié)果可知,在類的外面可以調(diào)用特殊方法。在編程時(shí)可以定義特殊方法來設(shè)計(jì)特殊功能。Python

語言已經(jīng)定義了一些特殊方法,這些方法是基類object

自帶的,如

init

()構(gòu)造方法用來初始化對象、

del

()析構(gòu)方法用來銷毀對象、

repr

()與

str

()方法用來打印對象等。下面對構(gòu)造方法和析構(gòu)方法進(jìn)行詳細(xì)介紹。1.

構(gòu)造方法構(gòu)造方法(即

init

()方法)是類中定義的特殊方法,該方法負(fù)責(zé)在創(chuàng)建對象時(shí)對對象進(jìn)行初始化。每個(gè)類都默認(rèn)有一個(gè)

init

()方法,如果一個(gè)類中顯式地定義了

init

()方法,那么在創(chuàng)建對象時(shí)調(diào)用顯式定義的

init

()方法;否則調(diào)用默認(rèn)的

init

()方法。

init

()方法可以分為無參構(gòu)造方法和有參構(gòu)造方法。下面定義一個(gè)包含無參構(gòu)造方法和對象方法test()的Car

類,示例代碼如程34序段P8.19

所示。P8.19

無參構(gòu)造方法class

Car:def

init

(self):self.color="red"def

test(self):print(f"車的顏色:{self.color}")car=Car()car.test()運(yùn)行代碼,輸出結(jié)果如下。車的顏色:red從運(yùn)行結(jié)果可以看出,對象

car

在調(diào)用test()方法時(shí)成功地訪問了color

屬第

8

類與對象35性,說明系統(tǒng)在創(chuàng)建這個(gè)對象的同時(shí)也調(diào)用

init

()方法對其進(jìn)行了初始化。下面定義一個(gè)包含有參構(gòu)造方法和對象方法test()的Car

類,示例代碼如程序段P8.20

所示。P8.20

有參構(gòu)造方法class

Car:def

init

(self,color):self.color=colordef

test(self):print(f"車的顏色:{self.color}")car=Car("white")car.test()運(yùn)行代碼,輸出結(jié)果如下。36車的顏色:white從運(yùn)行結(jié)果可以看出,當(dāng)通過帶有參構(gòu)造方法的類

Car

創(chuàng)建對象

car

時(shí),傳入的對應(yīng)參數(shù)white

成功地對類屬性color

進(jìn)行了初始化,對象car

在調(diào)用test()方法時(shí)成功地輸出了color

屬性的值。2.

析構(gòu)方法析構(gòu)方法(即

del

()方法)是銷毀對象時(shí)系統(tǒng)自動調(diào)用的特殊方法。每個(gè)類都默認(rèn)有一個(gè)

del

()方法。如果一個(gè)類中顯式地定義了

del

()方法,那么在銷毀該類對象時(shí)會調(diào)用顯式定義的

del

()方法;否則調(diào)用默認(rèn)的

del

()方法。下面定義一個(gè)包含構(gòu)造方法和析構(gòu)方法的Car

類,然后創(chuàng)建

Car

類的對象,之后分別在

del

語句執(zhí)行前后訪問

Car

類的對象的屬性,示例代碼如程序段P8.21

所示。第

8

類與對象37P8.21

析構(gòu)方法class

Car:def

init

(self):self.color="blue"print("對象被創(chuàng)建")def

del

(self):print("對象被銷毀")car=Car()print(car.color)delcarprint(car.color)運(yùn)行代碼,輸出結(jié)果如下。38對象被創(chuàng)建blue對象被銷毀Traceback(mostrecentcall

last):File"D:/w1.py",line10,in

<module>print(car.color)NameError:

name

'car'

is

not

defined.

Did

you

mean:

'Car'?從運(yùn)行結(jié)果可以看出,程序在刪除

Car

類的對象

car

之前成功地訪問了color

屬性;在刪除Car

類的對象car

后調(diào)用了析構(gòu)方法,打印“對象被銷毀”語句;在銷毀Car

類的對象car

后因無法使用Car

類的對象訪問屬性而出現(xiàn)錯誤信息。與文件類似,每個(gè)對象都會占用系統(tǒng)的一部分內(nèi)存,使用之后若不及時(shí)銷第

8

類與對象39毀,就會浪費(fèi)系統(tǒng)資源。那么對象什么時(shí)候銷毀呢?Python

通過引用計(jì)數(shù)器記錄所有對象的引用數(shù)量(可以理解為對象所占內(nèi)存的別名),一旦某個(gè)對象的引用計(jì)數(shù)器的值為

0,系統(tǒng)就會銷毀這個(gè)對象,并收回對象所占用的內(nèi)存空間。

8.4 Python

的對象體系在Python

中,對象可以分為元類對象(metaclass)、類對象(class)和實(shí)例對象(instance)。元類創(chuàng)建類,類創(chuàng)建實(shí)例。Python

是一種面向?qū)ο笳Z言,所有的對象都有所屬的類型。另外,在Python

中,一切皆對象,類型也是對象,因此,Python

的實(shí)例對象有各自所屬的類型,而類型對象給定的所屬類型就是type。type

本身也是對象,如果它屬于其他類型,那么其他類型又是對象,又需要所屬的類型,這樣下去就會是無窮了,因此,type

對象的類型只能是它本身。type

的二象性是指type

是所有類對象的類型,同時(shí)

type

也是一個(gè)類對象。所有的類對象都繼承object

類對象,type

是類對象所以它繼承object;所有40的類對象的類型都是type

類型,object

是類對象所以它的類型是type。因此,type

是元類,是創(chuàng)建所有類型的類;object

是基類,是所有類都要繼承的類。通過繼承type

還可以自定義元類。元類可以動態(tài)創(chuàng)建類。面向?qū)ο篌w系中有兩種關(guān)系:一種是類型關(guān)系,即對象所屬的類型,或者說對象是由哪個(gè)類型創(chuàng)建的;另一種是繼承關(guān)系。若把

Python

中的內(nèi)置類和用戶創(chuàng)建的類(如int、float、str、bool、tuple、dict、set

等)納入其中,就可以得到

Python

的對象體系圖(見圖

8.1),圖中顯示了所有類型關(guān)系和繼承關(guān)系。第

8

類與對象41圖

8.1

對象體系圖object

基類和type

元類是構(gòu)成Python

對象體系的基石。所有的對象都有一個(gè)生命周期,即創(chuàng)建、初始化、運(yùn)行和銷毀。Python

語言中元類的出現(xiàn)打破了很多其他面向?qū)ο笳Z言編碼時(shí)需要靜態(tài)聲明類的限制,Python

的對象模型結(jié)合了傳統(tǒng)類結(jié)構(gòu)語言的簡單性和其他模型語言的強(qiáng)大功能。428.4.1 object

基類每一個(gè)Python

類都隱含繼承了基類object?;愖詭У膶傩院头椒ㄈ绫?-1

所示。表

8-1基類自帶的屬性和方法屬性名與方法名功能描述

class

object.

class

,返回生成該對象的類

bases

object.

bases

,返回該對象的所有父類列表

dict

object.

dict

,返回該對象的所有屬性組成的字典

doc

object.

doc

,返回類的注釋說明

module

object.

module

,返回該對象所處的模塊

new

()在調(diào)用該類時(shí)自動觸發(fā),內(nèi)部會通過

new

產(chǎn)生一個(gè)新的對象第

8

類與對象43

init

()在調(diào)用類時(shí)自動觸發(fā),

new

產(chǎn)生的對象調(diào)用

init

(),初始化對象

getattr

()當(dāng)通過“對象.屬性”

獲取屬性時(shí),在“沒有該屬性”

時(shí)觸發(fā)

getattribute

()當(dāng)通過“對象.屬性”

獲取屬性時(shí),“無論有沒有該屬性都會觸發(fā)

setattr

()當(dāng)“對象.屬性=屬性值”時(shí),在“添加或修改屬性”時(shí)觸發(fā)

call

()在調(diào)用對象“對象

+

(

)”時(shí)觸發(fā)

str

()在“打印對象”時(shí)觸發(fā)

getitem

()在對象通過“對象[key]”獲取屬性時(shí)觸發(fā)

setitem

()在對象通過“對象[key]=value

值”獲取屬性時(shí)觸發(fā)”44

dir

()查看屬性和方法列表

delattr

()刪除屬性

eq

()等于

gt

()大于

lt

()小于

ge

()大于等于

le

()小于等于1.

基類的

new

()方法在使用類名()創(chuàng)建對象時(shí),Python

解釋器會自動調(diào)用

new

()方法和

init

()方法。Python

解釋器首先會調(diào)用

new

()方法在內(nèi)存中為對象分配內(nèi)存空間,然后返回對象的引用。Python

解釋器獲得對象的引用后,將引用作為第一個(gè)參數(shù)傳遞給

init

()方法,對對象進(jìn)行初始化。

new

()方法可以重第

8

類與對象45寫,重寫

new

()方法一定要返回

super().

new

(cls),否則Python

解釋器得不到分配了空間的對象引用,就不會調(diào)用對象的初始化方法。示例代碼如程序段P8.22

所示。P8.22

new

()方法無返回語句classDemo(object):def

init

(self):print("此處是

init

()方法的執(zhí)行...")def

new

(cls,*args,

**kwargs):print("此處是

new

()方法的執(zhí)行...")Demo()運(yùn)行代碼,輸出結(jié)果如下。此處是

new

()方法的執(zhí)行...46由于

new

()方法未返回對象,所有初始化方法

init

()都沒有執(zhí)行。為

new

()方法增加返回語句,修改代碼如程序段P8.23

所示。P8.23

new

()方法有返回語句class

Demo(object):def

init

(self):print("此處是

init

()方法的執(zhí)行...")def

new

(cls,*args,

**kwargs):print("此處是

new

()方法的執(zhí)行...")return

super().

new

(cls,*args,

**kwargs)Demo()運(yùn)行代碼,輸出結(jié)果如下。此處是

new

()方法的執(zhí)行...第

8

類與對象47此處是

init

()方法的執(zhí)行...

new

()方法增加返回語句后,調(diào)用了初始化方法

init

()。

new

()是一個(gè)靜態(tài)方法,在調(diào)用時(shí)需要主動傳遞cls

參數(shù)。2.

基類的

init

()方法

init

()方法在對象生命周期中起初始化作用,每個(gè)對象必須正確初始化后才能正常工作?;恛bject

有一個(gè)默認(rèn)包含pass

init

()實(shí)現(xiàn),創(chuàng)建對象后調(diào)用該方法初始化對象。子類可以不重寫

init

()方法,在實(shí)例化子類時(shí),會自動調(diào)用基類中已定義的

init

()方法,如果沒有對它進(jìn)行重寫,則在對象創(chuàng)建后就不會創(chuàng)建實(shí)例變量。示例代碼如程序段P8.24

所示。P8.24

不重寫

init

()方法創(chuàng)建對象class

Rectangle(object):def

area(self):48returnself.length*

self.widthr=Rectangle()r.length,r.width=13,

8print(r.area())運(yùn)行代碼,輸出結(jié)果如下。104此代碼在創(chuàng)建類時(shí),沒有重寫

init

()方法,Rectangle

類有一個(gè)使用兩個(gè)屬性來返回一個(gè)值的方法,這些屬性沒有初始化,不是實(shí)例變量。但這是合法的Python

代碼,它可以有效地避免專門設(shè)置屬性,增加了靈活性。雖然未初始化的屬性可能是有用的,但很有可能會帶來一些問題。“Python

之禪”中的建議是顯式比隱式更好。重寫

init

()方法實(shí)例化子類的情況就是上一章學(xué)習(xí)的內(nèi)容。示例代碼如程序段P8.25

所示。第

8

類與對象49P8.25

重寫

init

()方法class

Demo(object):def

init

(self):print("

init

()方法的執(zhí)行..")Demo()運(yùn)行代碼,輸出結(jié)果如下。

init

()方法的執(zhí)行..說明在創(chuàng)建對象時(shí)自動觸發(fā)調(diào)用了重寫的

init

()方法。3.

單例設(shè)計(jì)模式設(shè)計(jì)模式是前人工作的總結(jié)和提煉,是對相關(guān)代碼進(jìn)行高層次的抽象,是針對某一特定問題的成熟的解決方案。使用設(shè)計(jì)模式可以讓代碼更容易被他人理解、保證代碼的可靠性和可重用性。如果不學(xué)習(xí)設(shè)計(jì)模式,抽象能力肯定會50受到限制。因此,若要提高編程能力,就必須學(xué)習(xí)設(shè)計(jì)模式。目前,Python

主要有

23

種設(shè)計(jì)模式,單例模式是其中之一。單例設(shè)計(jì)模式確保類創(chuàng)建的對象在系統(tǒng)中只有唯一的一個(gè)實(shí)例,每次執(zhí)行類名()返回的對象,內(nèi)存地址是相同的。單例設(shè)計(jì)模式的應(yīng)用場景有音樂播放對象、回收站對象、打印機(jī)對象等。由于

Python

創(chuàng)建對象都是在

new

()

方法中實(shí)現(xiàn)的,

因此要重寫

new

()方法,定義一個(gè)類屬性,初始值是

None,用于記錄單例對象的引用。如果類屬性為

None,則調(diào)用父類方法分配空間(即創(chuàng)建對象),并在類屬性中記錄結(jié)果,返回類屬性中記錄的對象引用。下面以設(shè)計(jì)一個(gè)單例音樂播放器為例,示例代碼如程序段P8.26

所示。P8.26

單例設(shè)計(jì)模式class

MusicPlayer(object):instance=

None第

8

類與對象51init_flag=

Falsedef

new

(cls,*args,

**kwargs):ifcls.instanceis

None:cls.instance

=

super().

new

(cls)return

cls.instancedef

init

(self):ifnot

self.init_flag:print("初始化音樂播放器")self.init_flag=

Trueplayer1=

MusicPlayer()print(player1)player2=

MusicPlayer()print(player2)52運(yùn)行代碼,輸出結(jié)果如下。初始化音樂播放器<

main

.MusicPlayerobjectat

0x00000238D77972D0><

main

.MusicPlayerobjectat

0x00000238D77972D0>代碼中對

new

()方法進(jìn)行改造之后,每次都會得到第一次被創(chuàng)建對象的引用。在

init

()方法中定義一個(gè)類屬性init_flag,標(biāo)記是否執(zhí)行過初始化動作,初始值為False。在

init

()方法中,判斷init_flag,如果為False

就執(zhí)行初始化動作,然后將

init_flag

設(shè)置為

True,再次自動調(diào)用

init

()方法時(shí),初始化動作就不會被再次執(zhí)行了。這樣,在程序運(yùn)行期間,就只有一個(gè)音樂播放器對象,實(shí)現(xiàn)了單例模式設(shè)計(jì)。8.4.2 type

元類type

是Python

的一個(gè)內(nèi)建元類,它是一個(gè)比較特殊的類,所有數(shù)據(jù)類型第

8

類與對象53都是它的實(shí)例。type

不僅可以用來查看所有對象的類型,還可以用來創(chuàng)建類,其語法格式有以下兩種。type(obj)type(name,bases,

dict)第一種語法格式用來查看某個(gè)變量(類對象)的具體類型。其中,obj

表示某個(gè)變量或類對象。第二種語法格式用來創(chuàng)建類。其中,name

表示類的名稱;bases

表示一個(gè)元組,其中存儲的是該類的父類;dict

表示一個(gè)字典,用于表示類內(nèi)定義的屬性或方法。示例代碼如程序段

P8.27

所示。P8.27

type

創(chuàng)建類def

say(self):print("我愛Python!")54MyClass

=

type("MyClass",(object,),dict(say

=

say,

name

=

"Python

官方文檔"))myobj=

MyClass()myobj.say()print()運(yùn)行代碼,輸出結(jié)果如下。我愛Python!Python

官方文檔可以看到,此程序中通過

type()創(chuàng)建了類,其類名為

MyClass,繼承自object

類,而且該類中還包含一個(gè)say()方法和一個(gè)name

屬性。使用type()函數(shù)創(chuàng)建的類和直接使用class

定義的類并無差別。事實(shí)上,我們在使用class定義類時(shí),Python

解釋器底層依然是調(diào)用type()來創(chuàng)建這個(gè)類的。type

最高級的用法是創(chuàng)建元類,然后用新建的元類創(chuàng)建類。使用元類就是第

8

類與對象55為了在創(chuàng)建類時(shí)能夠動態(tài)地改變類中定義的屬性或方法,或者當(dāng)需要根據(jù)應(yīng)用場景為多個(gè)類添加某個(gè)屬性和方法時(shí),就可以先創(chuàng)建元類,然后用元類去創(chuàng)建類,最后使用該類的實(shí)例化對象實(shí)現(xiàn)功能。在創(chuàng)建新的元類時(shí),必須符合以下條件:必須顯式繼承自

type

類;類中需要定義并實(shí)現(xiàn)

new

()方法,該方法一定要返回該類的一個(gè)實(shí)例對象,因?yàn)樵谑褂迷悇?chuàng)建類時(shí),該

new

()方法會被自動執(zhí)行,用來修改新建的類。在用元類創(chuàng)建新的類時(shí),可以在標(biāo)注父類(或不標(biāo),默認(rèn)繼承自object)的同時(shí)指定元類(格式為metaclass=元類名),這樣,當(dāng)

Python

解釋器在創(chuàng)建該類時(shí),元類中的

new

()方法就會被調(diào)用,從而實(shí)現(xiàn)動態(tài)修改類屬性或類方法的目的。示例代碼如程序段P8.28

所示。P8.28

創(chuàng)建元類,再創(chuàng)建類class

MyMetaclass(type):def

new

(cls,name,bases,attrs):56attrs['name']=’李紅’attrs['say']=lambda

self:print('元類的say()方法')return

super().

new

(cls,name,bases,attrs)class

MyClass(object,metaclass=MyMetaclass):passmyobj=MyClass()print(myobj.name)myobj.say()運(yùn)行代碼,輸出結(jié)果如下。李紅元類的say()方法顯然,MyMetaclass

元類的

new

()方法動態(tài)地為

MyClass

類添加了第

8

類與對象57name

屬性和say()方法,因此,即便該類在定義時(shí)是空類,它也依然有name屬性和say()方法。metaclass

元類主要用于開發(fā)應(yīng)用編程接口(API)、開發(fā)框架層面的

Python庫等中間件,而在應(yīng)用開發(fā)層面,元類的使用范圍較小。type

自帶的屬性和方法如表

8-2

所示。表

8-2 type

自帶的屬性和方法屬性名與方法名功能描述

base

返回直接父類

bases

返回所有父類列表

basicsize

返回分配的內(nèi)存空間大小

flags

返回類型標(biāo)識

mro

返回類型方法解析順序元組58

name

返回類型名稱mro()class.mro(),返回類型的方法解析順序

instancecheck

()檢查對象是否為實(shí)例

prepare

()用于為類語句創(chuàng)建命名空間

sizeof

()返回類型對象占用的內(nèi)存字節(jié)數(shù)

subclasscheck

()檢查類是否為子類1.

元類的mro()方法對于支持繼承的編程語言來說,其方法(屬性)可能定義在當(dāng)前類,也可能來自于基類,因此,在調(diào)用方法時(shí)就需要對當(dāng)前類和基類進(jìn)行搜索以確定方法所在的位置。搜索的順序即方法解析順序(method

resolution

order,MRO)。對于Python

這種支持多繼承的語言來說,MRO

比較復(fù)雜。通過

mro

屬性和mro()方法可以查看方法解析順序,示例代碼如程序段P8.29

所示。第

8

類與對象59P8.29

查看方法解析順序class

A(object):def

show(self):print

("A.show()")classB(A):

passclass

C(A):def

show(self):print(

"C.show()")classD(B,C):

passprint(D.

mro

)print(D.mro())x=D()x.show()60運(yùn)行代碼,輸出結(jié)果如下。(<class

'

main

.D'>,

<class

'

main

.B'>,

<class

'

main

.C'>,

<class

'

main

.A'>,<class

'object'>)[<class

'

main

.D'>,

<class

'

main

.B'>,

<class

'

main

.C'>,

<class

'

main

.A'>,<class

'object'>]C.show()2.

元類的

instancecheck

()方法

instancecheck

()是專門用于isinstance()內(nèi)置函數(shù),檢測一個(gè)實(shí)例是否屬于某個(gè)類的實(shí)例。這個(gè)方法一定要定義在元類中。示例代碼如程序段P8.30所示。P8.30

檢測是否為類的實(shí)例class

MyMetaclass(type):第

8

類與對象61def

instancecheck

(cls,

instance):print("

instancecheck

call")return

hasattr(instance,

"

len

")class

Demo(metaclass=MyMetaclass):passdemo=Demo()print(isinstance(demo,Demo))運(yùn)行代碼,輸出結(jié)果如下。True62抽象類抽象類的使用方式抽象類是具有抽象方法的類。抽象方法是只有方法名而沒有實(shí)現(xiàn)的方法。抽象類是一種特殊的類,只能被繼承不能被實(shí)例化,子類需要實(shí)現(xiàn)基類指定的抽象方法。類是從現(xiàn)實(shí)對象抽象而來的,抽象類是基于類抽象而來的。抽象類的編程使得每個(gè)人都可以關(guān)注當(dāng)前抽象類的方法和描述,而不需要考慮過多的實(shí)現(xiàn)細(xì)節(jié),這對于協(xié)同開發(fā)具有重要意義,同時(shí)也提高了代碼的可讀性。在不同的模塊中通過抽象基類來調(diào)用,可以用最精簡的方式展示代碼之間的邏輯關(guān)系,使模塊之間的依賴關(guān)系清晰、簡單。一個(gè)抽象類可以有多個(gè)實(shí)現(xiàn),從而使得系統(tǒng)的運(yùn)轉(zhuǎn)更加靈活。抽象類的使用方式有兩種:一是通過直接繼承創(chuàng)建子類,直接繼承抽象基類的子類必須完全覆寫(實(shí)現(xiàn))抽象基類中的“抽象”內(nèi)容后,才能被實(shí)例化,抽第

8

類與對象63象基類中可以聲明“抽象方法”和“抽象屬性”;二是注冊虛擬子類,即將其他類“注冊”為抽象類的虛擬子類(調(diào)用

register

方法),這些虛擬子類不需要直接繼承自基類,它們可以選擇實(shí)現(xiàn)抽象基類中的部分API

接口,也可以選擇不做任何實(shí)現(xiàn),但是,在使用issubclass()和issubinstance()進(jìn)行判斷時(shí)仍然會返回真值。8.5.2

abc

模塊定義抽象類Python

本身不提供抽象類和接口機(jī)制,若想實(shí)現(xiàn)抽象類,則可以借助

abc模塊。abc

模塊在

Python

中定義了抽象基類

的組件,提供了一個(gè)特殊的metaclass(元類)——ABCMeta,還定義了一些裝飾器,如@abstractmethod和

@abstractproperty。ABCMeta

用于在Python

程序中創(chuàng)建抽象基類。抽象基類如果想要聲明“抽象方法”,則可以使用@abstractmethod,如果想要64聲明“抽象屬性”,則可以使用@abstractproperty。下面定義一個(gè)File

文件抽象類,然后創(chuàng)建

Txt

文本文件子類繼承文件File類。示例代碼如程序段P8.31

所示。P8.31 abc

模塊實(shí)現(xiàn)抽象類fromabcimport

ABCMetafromabcimportabstractmethodclassFile(metaclass=

ABCMeta):#

ABCMeta

元類實(shí)現(xiàn)抽象類#

定義抽象方法,無須實(shí)現(xiàn)功能@abstractmethoddef

read(self):pass#

子類繼承抽象類,必須實(shí)現(xiàn)抽象類中的read()方法class

Txt(File):def

read(self):print('文本數(shù)據(jù)的讀取方法')第

8

類與對象65txt=

Txt()txt.read()運(yùn)行代碼,輸出結(jié)果如下。文本數(shù)據(jù)的讀取方法子類

Txt

繼承抽象基類File,并實(shí)現(xiàn)了抽象方法read()。

8.6 封裝、繼承和多態(tài)8.6.1

封裝封裝是類和對象的基本特性,它的基本思想是對外隱藏類的細(xì)節(jié),提供用于訪問類成員的公開接口。類的外部無須知道類的細(xì)節(jié),只需要使用公開接口便可訪問類的內(nèi)容,因此,在一定程度上保證了類內(nèi)數(shù)據(jù)的安全。為了符合封裝思想,在定義類時(shí)需要滿足以下兩點(diǎn)要求。66(1)

將屬性聲明為私有屬性。(2)

添加兩個(gè)供外界調(diào)用的公有方法,分別用于設(shè)置和獲取私有屬性的值。下面結(jié)合以上兩點(diǎn)要求定義一個(gè)Person

類,示例代碼如程序段P8.32

所示。P8.32

類的封裝特性class

Person:def

init

(self,name):=nameself.

age=18def

set_age(self,new_age):if

0<new_age<=150:self.

age=new_agedef

get_age(self):第

8

類與對象67return

self.

ageperson=Person("小李")print(person.get_age())person.set_age(25)print(person.get_age())運(yùn)行代碼,輸出結(jié)果如下。1825由運(yùn)行結(jié)果可知,程序獲取的私有屬性

age

的值為

25,說明屬性值設(shè)置成功。由此可知,程序只能通過類提供的兩個(gè)公有方法訪問私有屬性,這既保證了類屬性的安全,又避免了隨意給屬性賦值的現(xiàn)象。688.6.2 繼承繼承是類和對象的重要特性之一,它主要用于描述類與類之間的關(guān)系,在不改變原有類的基礎(chǔ)上擴(kuò)展原有類的功能。若類與類之間具有繼承關(guān)系,則被繼承的類稱為父類或基類,繼承其他類的類稱為子類或派生類,子類會自動擁有父類的公有成員。1.

單繼承單繼承即子類只繼承一個(gè)父類。Python

中單繼承的語法格式如下。class

子類名(父類)子類在繼承父類的同時(shí)會自動擁有父類的公有成員。若在定義類時(shí)不指明該類的父類,那么該類默認(rèn)繼承基類object。下面定義一個(gè)狗類Dog

和一個(gè)繼承Dog

類的狼狗類WolfDog,示例代碼如程序段P8.33

所示。第

8

類與對象69P8.33

類的單繼承class

Dog(object):def

init

(self,color):self.color=colordef

walk(self):print("狗跳")class

WolfDog(Dog):passwolfdog=WolfDog("gray")print(wolfdog.color)wolfdog.walk()運(yùn)行代碼,輸出結(jié)果如下。70gray狗跳從以上結(jié)果可以看出,程序使用子類的對象成功地訪問了父類的屬性和方法,說明子類繼承父類后會自動擁有父類的公有成員。子類不會擁有父類的私有成員,也不能訪問父類的私有成員。在以上示例的Dog

類中增加一個(gè)私有屬性

age

和一個(gè)私有方法

test(),示例代碼如程序段P8.34

所示。P8.34

子類不會擁有父類的私有成員class

Dog(object):def

init

(self,color):self.color=colorself.

age=18第

8

類與對象71def

walk(self):print("狗跳")def

test(self):print("私有方法")class

WolfDog(Dog):passwolfdog=WolfDog("gray")print(wolfdog.

age)wolfdog.

test()運(yùn)行代碼,會出現(xiàn)如下所示的錯誤信息。Traceback(mostrecentcall

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論