當前位置:
首頁 > 知識 > Python面向對象(初級篇)

Python面向對象(初級篇)

點擊上方「

Python開發

」,選擇「置頂公眾號」


關鍵時刻,第一時間送達!






概述





  • 面向過程:根據業務邏輯從上到下寫壘代碼



  • 函數式:將某功能代碼封裝到函數中,日後便無需重複編寫,僅調用函數即可



  • 面向對象:對函數進行分類和封裝,讓開發「更快更好更強…」




面向過程編程最易被初學者接受,其往往用一長段代碼來實現指定功能,開發過程中最常見的操作就是粘貼複製,即:將之前實現的代碼塊複製到現需功能處。





while

 

True


    

if

 

cpu

利用率

 > 

90

%:

        

#發送郵件提醒


        

連接郵箱伺服器


        

發送郵件

        

關閉連接



    

if

 

硬碟使用空間

 > 

90

%:


        

#發送郵件提醒


        

連接郵箱伺服器


        

發送郵件


        

關閉連接



    

if

 

內存佔用

 > 

80

%:


        

#發送郵件提醒


        

連接郵箱伺服器


        

發送郵件


        

關閉連接




隨著時間的推移,開始使用了函數式編程,增強代碼的重用性和可讀性,就變成了這樣:





def

 

發送郵件

(

內容

)


    

#發送郵件提醒


    

連接郵箱伺服器


    

發送郵件


    

關閉連接



while

 

True



    

if

 

cpu

利用率

 > 

90

%:


        

發送郵件

(

"CPU報警"

)



    

if

 

硬碟使用空間

 > 

90

%:


        

發送郵件

(

"硬碟報警"

)



    

if

 

內存佔用

 > 

80

%:


        

發送郵件

(

"內存報警"

)




今天我們來學習一種新的編程方式:面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)。




創建類和對象




面向對象編程是一種編程方式,此編程方式的落地需要使用 「類」 和 「對象」 來實現,所以,面向對象編程其實就是對 「類」 和 「對象」 的使用。






  • 類就是一個模板,模板里可以包含多個函數,函數里實現一些功能



  • 對象則是根據模板創建的實例,通過實例對象可以執行類中的函數









  • class是關鍵字,表示類



  • 創建對象,類名稱後加括弧即可




ps:類中的函數第一個參數必須是self(詳細見:類的三大特性之封裝)


   類中定義的函數叫做 「方法」





# 創建類


class

 

Foo

:


    


    

def Bar

(

self

)

:


        

print

 

"Bar"



    

def Hello

(

self

,

 

name

)

:


        

print

 

"i am %s"

 %

name



# 根據類Foo創建對象obj


obj

 = 

Foo

()


obj

.

Bar

()

            

#執行Bar方法


obj

.

Hello

(

"wupeiqi"

)

 

#執行Hello方法




誒,你在這裡是不是有疑問了?使用函數式編程和面向對象編程方式來執行一個「方法」時函數要比面向對象簡便。






  • 面向對象:【創建對象】【通過對象執行方法】



  • 函數編程:【執行函數】




觀察上述對比答案則是肯定的,然後並非絕對,場景的不同適合其的編程方式也不同。




總結:函數式的應用場景 –> 各個函數之間是獨立且無共用的數據。




面向對象三大特性




面向對象的三大特性是指:封裝、繼承和多態。




一、封裝




封裝,顧名思義就是將內容封裝到某個地方,以後再去調用被封裝在某處的內容。




所以,在使用面向對象的封裝特性時,需要:






  • 將內容封裝到某處



  • 從某處調用被封裝的內容




第一步:將內容封裝到某處







self 是一個形式參數,當執行 obj1 = Foo("wupeiqi", 18 ) 時,self 等於 obj1




                              當執行 obj2 = Foo("alex", 78 ) 時,self 等於 obj2




所以,內容其實被封裝到了對象 obj1 和 obj2 中,每個對象中都有 name 和 age 屬性,在內存里類似於下圖來保存。







第二步:從某處調用被封裝的內容




調用被封裝的內容時,有兩種情況:






  • 通過對象直接調用



  • 通過self間接調用




1、通過對象直接調用被封裝的內容




上圖展示了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式可以如此調用被封裝的內容:對象.屬性名





class

 

Foo

:



    

def __init__

(

self

,

 

name

,

 

age

)

:


        

self

.

name

 = 

name


        

self

.

age

 = 

age



obj1

 = 

Foo

(

"wupeiqi"

,

 

18

)


print 

obj1

.

name

    

# 直接調用obj1對象的name屬性


print 

obj1

.

age

     

# 直接調用obj1對象的age屬性



obj2

 = 

Foo

(

"alex"

,

 

73

)


print 

obj2

.

name

    

# 直接調用obj2對象的name屬性


print 

obj2

.

age

     

# 直接調用obj2對象的age屬性




2、通過self間接調用被封裝的內容




執行類中的方法時,需要通過self間接調用被封裝的內容





class

 

Foo

:


  


    

def __init__

(

self

,

 

name

,

 

age

)

:


        

self

.

name

 = 

name


        

self

.

age

 = 

age


  


    def detail

(

self

)

:


        

print 

self

.

name


        print 

self

.

age


  


obj1

 = 

Foo

(

"wupeiqi"

,

 

18

)


obj1

.

detail

()

  

# Python默認會將obj1傳給self參數,即:obj1.detail(obj1),所以,此時方法內部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18


  


obj2

 = 

Foo

(

"alex"

,

 

73

)


obj2

.

detail

()

  

# Python默認會將obj2傳給self參數,即:obj1.detail(obj2),所以,此時方法內部的 self = obj2,即:self.name 是 alex ; self.age 是 78




綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然後通過對象直接或者self間接獲取被封裝的內容。





練習一:在終端輸出如下信息






  • 小明,10歲,男,上山去砍柴



  • 小明,10歲,男,開車去東北



  • 小明,10歲,男,最愛大保健



  • 老李,90歲,男,上山去砍柴



  • 老李,90歲,男,開車去東北



  • 老李,90歲,男,最愛大保健



  • 老張…





def kanchai

(

name

,

 

age

,

 

gender

)

:


    

print

 

"%s,%s歲,%s,上山去砍柴"

 %

(

name

,

 

age

,

 

gender

)


 


 


def qudongbei

(

name

,

 

age

,

 

gender

)

:


    

print

 

"%s,%s歲,%s,開車去東北"

 %

(

name

,

 

age

,

 

gender

)


 


 


def dabaojian

(

name

,

 

age

,

 

gender

)

:


    

print

 

"%s,%s歲,%s,最愛大保健"

 %

(

name

,

 

age

,

 

gender

)


 


 


kanchai

(

"小明"

,

 

10

,

 

"男"

)


qudongbei

(

"小明"

,

 

10

,

 

"男"

)


dabaojian

(

"小明"

,

 

10

,

 

"男"

)


 


 


kanchai

(

"老李"

,

 

90

,

 

"男"

)


qudongbei

(

"老李"

,

 

90

,

 

"男"

)


dabaojian

(

"老李"

,

 

90

,

 

"男"

)





class

 

Foo

:


  


    

def __init__

(

self

,

 

name

,

 

age

 

,

gender

)

:


        

self

.

name

 = 

name


        

self

.

age

 = 

age


        

self

.

gender

 = 

gender


 


    def kanchai

(

self

)

:


        

print

 

"%s,%s歲,%s,上山去砍柴"

 %

(

self

.

name

,

 

self

.

age

,

 

self

.

gender

)


 


    

def qudongbei

(

self

)

:


        

print

 

"%s,%s歲,%s,開車去東北"

 %

(

self

.

name

,

 

self

.

age

,

 

self

.

gender

)


 


    

def dabaojian

(

self

)

:


        

print

 

"%s,%s歲,%s,最愛大保健"

 %

(

self

.

name

,

 

self

.

age

,

 

self

.

gender

)


 


 


xiaoming

 = 

Foo

(

"小明"

,

 

10

,

 

"男"

)


xiaoming

.

kanchai

()


xiaoming

.

qudongbei

()


xiaoming

.

dabaojian

()


 


laoli

 = 

Foo

(

"老李"

,

 

90

,

 

"男"

)


laoli

.

kanchai

()


laoli

.

qudongbei

()


laoli

.

dabaojian

()




上述對比可以看出,如果使用函數式編程,需要在每次執行函數時傳入相同的參數,如果參數多的話,又需要粘貼複製了… ;而對於面向對象只需要在創建對象時,將所有需要的參數封裝到當前對象中,之後再次使用時,通過self間接去當前對象中取值即可。





練習二:遊戲人生程序




1、創建三個遊戲人物,分別是:




蒼井井,女,18,初始戰鬥力1000


東尼木木,男,20,初始戰鬥力1800


波多多,女,19,初始戰鬥力2500


2、遊戲場景,分別:




草叢戰鬥,消耗200戰鬥力


自我修鍊,增長100戰鬥力


多人遊戲,消耗500戰鬥力





# -*- coding:utf-8 -*-


 


# #####################  定義實現功能的類  #####################


 


class

 

Person

:


 


    

def __init__

(

self

,

 

na

,

 

gen

,

 

age

,

 

fig

)

:


        

self

.

name

 = 

na


        

self

.

gender

 = 

gen


        

self

.

age

 = 

age


        

self

.

fight

 =

fig


 


    def grassland

(

self

)

:


        

"""注釋:草叢戰鬥,消耗200戰鬥力"""


 


        

self

.

fight

 = 

self

.

fight

 - 

200


 


    

def practice

(

self

)

:


        

"""注釋:自我修鍊,增長100戰鬥力"""


        


        

self

.

fight

 = 

self

.

fight

 + 

200


 


    

def incest

(

self

)

:


        

"""注釋:多人遊戲,消耗500戰鬥力"""


        


        

self

.

fight

 = 

self

.

fight

 - 

500


 


    

def detail

(

self

)

:


        

"""注釋:當前對象的詳細情況"""


 


        

temp

 = 

"姓名:%s ; 性別:%s ; 年齡:%s ; 戰鬥力:%s"

  % 

(

self

.

name

,

 

self

.

gender

,

 

self

.

age

,

 

self

.

fight

)


        

print 

temp


 


        


# #####################  開始遊戲  #####################


 


cang

 = 

Person

(

"蒼井井"

,

 

"女"

,

 

18

,

 

1000

)

    

# 創建蒼井井角色


dong

 = 

Person

(

"東尼木木"

,

 

"男"

,

 

20

,

 

1800

)

  

# 創建東尼木木角色


bo

 = 

Person

(

"波多多"

,

 

"女"

,

 

19

,

 

2500

)

      

# 創建波多多角色


 


cang

.

incest

()

 

#蒼井空參加一次多人遊戲


dong

.

practice

()

#東尼木木自我修鍊了一次


bo

.

grassland

()

 

#波多多參加一次草叢戰鬥


 


 


#輸出當前所有人的詳細情況


cang

.

detail

()


dong

.

detail

()


bo

.

detail

()


 


 


cang

.

incest

()

 

#蒼井空又參加一次多人遊戲


dong

.

incest

()

 

#東尼木木也參加了一個多人遊戲


bo

.

practice

()

 

#波多多自我修鍊了一次


 


#輸出當前所有人的詳細情況


cang

.

detail

()


dong

.

detail

()


bo

.

detail

()




二、繼承




繼承,面向對象中的繼承和現實生活中的繼承相同,即:子可以繼承父的內容。




例如:




貓可以:喵喵叫、吃、喝、拉、撒




狗可以:汪汪叫、吃、喝、拉、撒




如果我們要分別為貓和狗創建一個類,那麼就需要為 貓 和 狗 實現他們所有的功能,如下所示:





class

 

貓:


 


    

def

 

喵喵叫

(

self

)

:


        

print

 

"喵喵叫"


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


class

 

狗:


 


    

def

 

汪汪叫

(

self

)

:


        

print

 

"喵喵叫"


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something




上述代碼不難看出,吃、喝、拉、撒是貓和狗都具有的功能,而我們卻分別的貓和狗的類中編寫了兩次。如果使用 繼承 的思想,如下實現:




動物:吃、喝、拉、撒




貓:喵喵叫(貓繼承動物的功能)




狗:汪汪叫(狗繼承動物的功能)





class

 

動物

:


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


    

def

 

(

self

)

:


        

# do something


 


# 在類後面括弧中寫入另外一個類名,表示當前類繼承另外一個類


class

 

(

動物

)


 


    

def

 

喵喵叫

(

self

)

:


        

print

 

"喵喵叫"


        


# 在類後面括弧中寫入另外一個類名,表示當前類繼承另外一個類


class

 

(

動物

)


 


    

def

 

汪汪叫

(

self

)

:


        

print

 

"喵喵叫"




所以,對於面向對象的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。




註:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不同而已。







學習了繼承的寫法之後,我們用代碼來是上述阿貓阿狗的功能:





class

 

Animal

:


 


    

def eat

(

self

)

:


        

print

 

"%s 吃 "

 %

self

.

name


 


    def drink

(

self

)

:


        

print

 

"%s 喝 "

 %

self

.

name


 


    def shit

(

self

)

:


        

print

 

"%s 拉 "

 %

self

.

name


 


    def pee

(

self

)

:


        

print

 

"%s 撒 "

 %

self

.

name


 


 


class

 

Cat

(

Animal

)

:


 


    

def __init__

(

self

,

 

name

)

:


        

self

.

name

 = 

name


        

self

.

breed

 

 

"貓"


 


    

def cry

(

self

)

:


        

print

 

"喵喵叫"


 


class

 

Dog

(

Animal

)

:


    


    

def __init__

(

self

,

 

name

)

:


        

self

.

name

 = 

name


        

self

.

breed

 

 

"狗"


        


    

def cry

(

self

)

:


        

print

 

"汪汪叫"


        


 


# ######### 執行 #########


 


c1

 = 

Cat

(

"小白家的小黑貓"

)


c1

.

eat

()


 


c2

 = 

Cat

(

"小黑的小白貓"

)


c2

.

drink

()


 


d1

 = 

Dog

(

"胖子家的小瘦狗"

)


d1

.

eat

()




那麼問題又來了,多繼承呢?






  • 是否可以繼承多個類



  • 如果繼承的多個類每個類中都定了相同的函數,那麼那一個會被使用呢?




1、Python的類可以繼承多個類,Java和C#中則只能繼承一個類




2、Python的類如果繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先和廣度優先









  • 當類是經典類時,多繼承情況下,會按照深度優先方式查找



  • 當類是新式類時,多繼承情況下,會按照廣度優先方式查找




經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 當前類或者父類繼承了object類,那麼該類便是新式類,否則便是經典類。











class

 

D

:


 


    

def bar

(

self

)

:


        

print

 

"D.bar"


 


 


class

 

C

(

D

)

:


 


    

def bar

(

self

)

:


        

print

 

"C.bar"


 


 


class

 

B

(

D

)

:


 


    

def bar

(

self

)

:


        

print

 

"B.bar"


 


 


class

 

A

(

B

,

 

C

)

:


 


    

def bar

(

self

)

:


        

print

 

"A.bar"


 


a

 = 

A

()


# 執行bar方法時


# 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中么有,則繼續去D類中找,如果D類中么有,則繼續去C類中找,如果還是未找到,則報錯


# 所以,查找順序:A --> B --> D --> C


# 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了


a

.

bar

()




經典類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中么有,則繼續去D類中找,如果D類中么有,則繼續去C類中找,如果還是未找到,則報錯




新式類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中么有,則繼續去C類中找,如果C類中么有,則繼續去D類中找,如果還是未找到,則報錯




注意:在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了。




三、多態




Pyhon不支持Java和C#這一類強類型語言中多態的寫法,但是原生多態,其Python崇尚「鴨子類型」。





# Python偽代碼實現Java或C#的多態




class

 

F1

:


    

pass


 


 


class

 

S1

(

F1

)

:


 


    

def show

(

self

)

:


        

print

 

"S1.show"


 


 


class

 

S2

(

F1

)

:


 


    

def show

(

self

)

:


        

print

 

"S2.show"


 


 


# 由於在Java或C#中定義函數參數時,必須指定參數的類型


# 為了讓Func函數既可以執行S1對象的show方法,又可以執行S2對象的show方法,所以,定義了一個S1和S2類的父類


# 而實際傳入的參數是:S1對象和S2對象


 


def Func

(

F1 

obj

)

:


    

"""Func函數需要接收一個F1類型或者F1子類的類型"""


    


    

print 

obj

.

show

()


    


s1_obj

 = 

S1

()


Func

(

s1_obj

)

 

# 在Func函數中傳入S1類的對象 s1_obj,執行 S1 的show方法,結果:S1.show


 


s2_obj

 = 

S2

()


Func

(

s2_obj

)

 

# 在Func函數中傳入Ss類的對象 ss_obj,執行 Ss 的show方法,結果:S2.show





# Python 「鴨子類型」



class

 

F1

:


    

pass


 


 


class

 

S1

(

F1

)

:


 


    

def show

(

self

)

:


        

print

 

"S1.show"


 


 


class

 

S2

(

F1

)

:


 


    

def show

(

self

)

:


        

print

 

"S2.show"


 


def Func

(

obj

)

:


    

print 

obj

.

show

()


 


s1_obj

 = 

S1

()


Func

(

s1_obj

)


 


s2_obj

 = 

S2

()


Func

(

s2_obj

)




總結




以上就是本節對於面向對象初級知識的介紹,總結如下:




面向對象是一種編程方式,此編程方式的實現是基於對 類 和 對象 的使用


類 是一個模板,模板中包裝了多個「函數」供使用


對象,根據模板創建的實例(即:對象),實例用於調用被包裝在類中的函數


面向對象三大特性:封裝、繼承和多態


問答專區




問題一:什麼樣的代碼才是面向對象?




答:從簡單來說,如果程序中的所有功能都是用 類 和 對象 來實現,那麼就是面向對象編程了。




問題二:函數式編程 和 面向對象 如何選擇?分別在什麼情況下使用?




答:須知:對於 C# 和 Java 程序員來說不存在這個問題,因為該兩門語言只支持面向對象編程(不支持函數式編程)。而對於 Python 和 PHP 等語言卻同時支持兩種編程方式,且函數式編程能完成的操作,面向對象都可以實現;而面向對象的能完成的操作,函數式編程不行(函數式編程無法實現面向對象的封裝功能)。




所以,一般在Python開發中,全部使用面向對象 或 面向對象和函數式混合使用




面向對象的應用場景:




1. 多函數需使用共同的值,如:資料庫的增、刪、改、查操作都需要連接資料庫字元串、主機名、用戶名和密碼





class

 

SqlHelper

:


 


    

def __init__

(

self

,

 

host

,

 

user

,

 

pwd

)

:


 


        

self

.

host

 = 

host


        

self

.

user

 = 

user


        

self

.

pwd

 = 

pwd


 


    

def

 

(

self

)

:


        

# 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接


        

# do something


        

# 關閉資料庫連接


 


    

def

 

(

self

)

:


        

# 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接


        

# do something


        

# 關閉資料庫連接


 


    

def

 

(

self

)

:


        

# 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接


        

# do something


        

# 關閉資料庫連接


 


    

def

 

(

self

)

:


    

# 使用主機名、用戶名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接


        

# do something


        

# 關閉資料庫連接# do something




2. 需要創建多個事物,每個事物屬性個數相同,但是值的需求




如:張三、李四、楊五,他們都有姓名、年齡、血型,但其都是不相同。即:屬性個數相同,但值不相同





class

 

Person

:


 


    

def __init__

(

self

,

 

name

 

,

age

 

,

blood_type

)

:


 


        

self

.

name

 = 

name


        

self

.

age

 = 

age


        

self

.

blood_type

 = 

blood_type


 


 


    def detail

(

self

)

:


        

temp

 = 

"i am %s, age %s , blood type %s "

 % 

(

self

.

name

,

 

self

.

age

,

 

self

.

blood_type

)


        

print temp


 


zhangsan

 = 

Person

(

"張三"

,

 

18

,

 

"A"

)


lisi

 = 

Person

(

"李四"

,

 

73

,

 

"AB"

)


yangwu

 = 

Person

(

"楊五"

,

 

84

,

 

"A"

)




問題三:類和對象在內存中是如何保存?




答:類以及類中的方法在內存中只有一份,而根據類創建的每一個對象都在內存中需要存一份,大致如下圖:







如上圖所示,根據類創建對象時,對象中除了封裝 name 和 age 的值之外,還會保存一個類對象指針,該值指向當前對象的類。




當通過 obj1 執行 【方法一】 時,過程如下:






  • 根據當前對象中的 類對象指針 找到類中的方法



  • 將對象 obj1 當作參數傳給 方法的第一個參數 self







  • 作者:武沛齊 



  • 出處:http://www.cnblogs.com/wupeiqi/ 



  • 鏈接:www.cnblogs.com/wupeiqi/p/4493506.html



  • Python開發整理髮布,轉載請聯繫作者獲得授權


【點擊聽勁爆情歌】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

高逼格程序員指南
教女朋友學Python是道送命題
從娘胎里開始學Python
Fedora需要幾年時間才能從Python 2切換到Python 3 ?
調查報告:Python成最熱門工具!近半程序員認為上級技術水平不如自己!

TAG:Python開發 |

您可能感興趣

Python指南:面向對象程序設計
TensorFlow官方最新tf.keras指南:面向對象構建深度網路
Perl 面向對象
學慣用 Thonny 寫代碼:一個面向初學者的Python IDE
Bantam Tools 桌面 PCB 銑床通過 Digi-Key 面向全球即時供貨
E3:《煮糊了2》面向PS4/Xbox One/Switch/PC公布
三星或正在研發第二款Bixby智能音箱,對標Google Home Mini面向低端市場
Google發布面向Linux和Mac的VR180 Creator
思科聯合Pure Storage發布面向AI的FlashStack融合系統
微軟Chromium版Edge瀏覽器Dev版面向Windows 7/8.1推出
AppleCare+ for Mac正式面向中國市場推出
Facebook Reality Labs正式成立面向VR/AR開發
Google只發布面向Linux和Mac的VR180 Creator
Dell宣布與Google合作:面向商務領域推出Chromebook
Surface Phone:ARW處理器和觸屏筆,將面向高端市場開放
《Arena of Valor》NS版本周末首次內測 僅面向歐洲地區開放
Matterport公司面向開發者推出3D SDK
KDD2018最佳論文:首個面向Facebook、arXiv網路圖類對抗攻擊研究
蘋果AppleCare+for Mac正式面向中國市場推出
Cloud IoT Core 現已面向公眾發布