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

下載本文檔

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

文檔簡介

8

類 與 對 象XXXX

大學XX

學院XXX

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

8

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

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

使用class

關鍵字來定義一個類。類的語法格式如下。class

類名:屬性名

=

屬性值def

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

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

1

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

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

所示。P8.1

自定義類class

Car:color="red"def

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

8

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

=

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

類創(chuàng)建一個對象,代碼如下。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()運行代碼,輸出結(jié)果如下。red行駛

8.2 屬性8.2.1

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

8

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

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

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

屬性就是一個類屬性。類屬性可以通過類對象進行訪問,但只能通過類進行修改。例如,定義一個只包含類屬性的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)運行代碼,輸出結(jié)果如下。redredwhite第

8

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

類和car

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

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

類和car

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

car

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

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

car

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

black

”?

因為“car.color='black'”語句起到了添加一個與類屬性同名的對象屬性的作用。2.

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

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

3

個方面對對象屬性進行介紹。1)

訪問對象屬性對象屬性只能通過對象進行訪問。例如,定義一個包含方法和對象屬性的類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運行代碼,輸出結(jié)果如下。white以上代碼在drive()方法中使用self

關鍵字定義了一個顏色屬性;通過對象car

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

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

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

<module>print(Car.color)AttributeError:

type

object

'Car'

has

no

attribute

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

car

成功地訪問了對象屬性,當通過類

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

修改對象屬性對象屬性通過對象進行修改。例如,在以上示例中修改對象屬性的代碼如程序段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)運行代碼,輸出結(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)運行代碼,輸出結(jié)果如下。white4程序成功地添加了對象屬性,并通過對象訪問了新增加的對象屬性。8.2.2

公有屬性與私有屬性Python

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

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

8

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

所示。P8.6

定義私有屬性class

Car:

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

color)運行代碼,輸出結(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()運行代碼,輸出結(jié)果如下。車的顏色是white第

8

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

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

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

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

所示。P8.8

定義特殊屬性class

Car:

color

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

color

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

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

dict

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

class

表示對象所屬的類等。

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

方法Python

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

方法

4

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

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

self

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

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()運行代碼,輸出結(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ù)列表的第一個參數(shù)為cls,代表類本身,它會在類方法被調(diào)用第

8

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

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

所示。P8.10

定義類方法class

Car:@classmethoddef

stop(cls):print('這是類方法')car=Car()car.stop()Car.stop()運行代碼,輸出結(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運行代碼,輸出結(jié)果如下。blackred從以上結(jié)果可以看出,程序在類方法stop()中成功地訪問和修改了類屬性color

的值。3.

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

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

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

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

所示。P8.12

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

Car:@staticmethoddef

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

8

類與對象25這是靜態(tài)方法這是靜態(tài)方法由運行結(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()運行代碼,輸出結(jié)果如下。這是靜態(tài)方法類屬性color

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

property

方法@property

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

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

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

方法名():第

8

類與對象27

方法體例如,定義一個帶有年齡私有屬性的人類,并定義一個@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運行代碼,輸出結(jié)果為

18。通過不帶括號的age

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

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

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

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

屬性添加setter()方法,這時就要用到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)運行代碼,輸出結(jié)果為

18,25。通過age

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

公有方法與私有方法Python

語言類的方法的可見度有兩種:公有方法和私有方法。默認是公有方法,可以在類的外部通過類或?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()運行代碼,輸出結(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()運行代碼,輸出結(jié)果如下。行駛32由運行結(jié)果可知,通過公有方法test

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

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

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

所示。P8.18

定義特殊方法class

Car:def

drive

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

drive

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

8

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

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

自帶的,如

init

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

del

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

repr

()與

str

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

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

init

()方法)是類中定義的特殊方法,該方法負責在創(chuàng)建對象時對對象進行初始化。每個類都默認有一個

init

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

init

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

init

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

init

()方法。

init

()方法可以分為無參構(gòu)造方法和有參構(gòu)造方法。下面定義一個包含無參構(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()運行代碼,輸出結(jié)果如下。車的顏色:red從運行結(jié)果可以看出,對象

car

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

屬第

8

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

init

()方法對其進行了初始化。下面定義一個包含有參構(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()運行代碼,輸出結(jié)果如下。36車的顏色:white從運行結(jié)果可以看出,當通過帶有參構(gòu)造方法的類

Car

創(chuàng)建對象

car

時,傳入的對應參數(shù)white

成功地對類屬性color

進行了初始化,對象car

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

屬性的值。2.

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

del

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

del

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

del

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

del

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

del

()方法。下面定義一個包含構(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)運行代碼,輸出結(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'?從運行結(jié)果可以看出,程序在刪除

Car

類的對象

car

之前成功地訪問了color

屬性;在刪除Car

類的對象car

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

類的對象car

后因無法使用Car

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

8

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

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

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

8.4 Python

的對象體系在Python

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

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

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

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

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

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

的二象性是指type

是所有類對象的類型,同時

type

也是一個類對象。所有的類對象都繼承object

類對象,type

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

類型,object

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

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

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

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

Python

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

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

Python

的對象體系圖(見圖

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

8

類與對象41圖

8.1

對象體系圖object

基類和type

元類是構(gòu)成Python

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

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

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

基類每一個Python

類都隱含繼承了基類object。基類自帶的屬性和方法如表8-1

所示。表

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

class

object.

class

,返回生成該對象的類

bases

object.

bases

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

dict

object.

dict

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

doc

object.

doc

,返回類的注釋說明

module

object.

module

,返回該對象所處的模塊

new

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

new

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

8

類與對象43

init

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

new

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

init

(),初始化對象

getattr

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

獲取屬性時,在“沒有該屬性”

時觸發(fā)

getattribute

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

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

setattr

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

call

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

+

(

)”時觸發(fā)

str

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

getitem

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

setitem

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

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

dir

()查看屬性和方法列表

delattr

()刪除屬性

eq

()等于

gt

()大于

lt

()小于

ge

()大于等于

le

()小于等于1.

基類的

new

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

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

new

()方法和

init

()方法。Python

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

new

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

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

init

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

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()運行代碼,輸出結(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()運行代碼,輸出結(jié)果如下。此處是

new

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

8

類與對象47此處是

init

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

new

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

init

()。

new

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

參數(shù)。2.

基類的

init

()方法

init

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

有一個默認包含pass

init

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

init

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

init

()方法,如果沒有對它進行重寫,則在對象創(chuàng)建后就不會創(chuàng)建實例變量。示例代碼如程序段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())運行代碼,輸出結(jié)果如下。104此代碼在創(chuàng)建類時,沒有重寫

init

()方法,Rectangle

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

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

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

init

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

所示。第

8

類與對象49P8.25

重寫

init

()方法class

Demo(object):def

init

(self):print("

init

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

init

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

init

()方法。3.

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

主要有

23

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

Python

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

new

()

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

因此要重寫

new

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

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

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

所示。P8.26

單例設計模式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運行代碼,輸出結(jié)果如下。初始化音樂播放器<

main

.MusicPlayerobjectat

0x00000238D77972D0><

main

.MusicPlayerobjectat

0x00000238D77972D0>代碼中對

new

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

init

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

init

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

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

init_flag

設置為

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

init

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

元類type

是Python

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

8

類與對象53都是它的實例。type

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

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

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

表示類的名稱;bases

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

表示一個字典,用于表示類內(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()運行代碼,輸出結(jié)果如下。我愛Python!Python

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

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

MyClass,繼承自object

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

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

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

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

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

8

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

type

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

new

()方法,該方法一定要返回該類的一個實例對象,因為在使用元類創(chuàng)建類時,該

new

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

Python

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

new

()方法就會被調(diào)用,從而實現(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()運行代碼,輸出結(jié)果如下。李紅元類的say()方法顯然,MyMetaclass

元類的

new

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

MyClass

類添加了第

8

類與對象57name

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

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

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

自帶的屬性和方法如表

8-2

所示。表

8-2 type

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

base

返回直接父類

bases

返回所有父類列表

basicsize

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

flags

返回類型標識

mro

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

name

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

instancecheck

()檢查對象是否為實例

prepare

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

sizeof

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

subclasscheck

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

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

resolution

order,MRO)。對于Python

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

比較復雜。通過

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運行代碼,輸出結(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ù),檢測一個實例是否屬于某個類的實例。這個方法一定要定義在元類中。示例代碼如程序段P8.30所示。P8.30

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

8

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

register

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

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

abc

模塊定義抽象類Python

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

abc模塊。abc

模塊在

Python

中定義了抽象基類

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

@abstractproperty。ABCMeta

用于在Python

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

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

Txt

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

所示。P8.31 abc

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

ABCMetafromabcimportabstractmethodclassFile(metaclass=

ABCMeta):#

ABCMeta

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

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

read(self):pass#

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

Txt(File):def

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

8

類與對象65txt=

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

Txt

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

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

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

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

添加兩個供外界調(diào)用的公有方法,分別用于設置和獲取私有屬性的值。下面結(jié)合以上兩點要求定義一個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())運行代碼,輸出結(jié)果如下。1825由運行結(jié)果可知,程序獲取的私有屬性

age

的值為

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

單繼承單繼承即子類只繼承一個父類。Python

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

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

和一個繼承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()運行代碼,輸出結(jié)果如下。70gray狗跳從以上結(jié)果可以看出,程序使用子類的對象成功地訪問了父類的屬性和方法,說明子類繼承父類后會自動擁有父類的公有成員。子類不會擁有父類的私有成員,也不能訪問父類的私有成員。在以上示例的Dog

類中增加一個私有屬性

age

和一個私有方法

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()運行代碼,會出現(xiàn)如下所示的錯誤信息。Traceback(mostrecentcall

溫馨提示

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

評論

0/150

提交評論