1. 程式人生 > >python之面向對象編程一

python之面向對象編程一

業務 支持 學習 som 一份 廣度優先 show 設計 繼續

概述:

  • 面向過程:根據業務邏輯從上到下寫壘代碼
  • 函數式:將某功能代碼封裝到函數中,日後便無需重復編寫,僅調用函數即可
  • 面向對象:對函數進行分類和封裝,讓開發“更快更好更強...”

面向過程編程最易被初學者接受,其往往用一長段代碼來實現指定功能,開發過程中最常見的操作就是粘貼復制,即:將之前實現的代碼塊復制到現需功能處。隨著時間的推移,開始使用了函數式編程,增強代碼的重用性和可讀性。今天學習新的編程方式:面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)

創建類和對象

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

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

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

1 class people:
2     def wave(self):
3         print(Hello World!)
4 
5 obj = people()

class為關鍵字,表示創建一個people的類 ,def wave為創建類中的函數,obj則是class創建的實例對象。

ps:類中的函數第一個參數必須是self, 類中定義的函數叫做 “方法”。

class people:
    def jon(self):
        
print(say Hello!) def tom(self,name): print(say hello %s%name) obj = people() obj.jon() #執行jon方法 obj.tom(Jerry) #執行tom方法

通過上面代碼演示,你會認為使用函數式編程和面向對象編程方式來執行一個“方法”時函數要比面向對象簡便

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

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

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

面向對象的三大特性

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

一、封裝

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

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

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

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

# 創建類
class people:
    def __init__(self,name,age):
        self.name = name
        self.age = age
#根據類people創建對象
#自動執行people類的__init__方法
p1 = people(xixi,21)
p2 = people(hehe,23)

上面代碼__init__()稱為構造方法,根據類創建對象時自動執行

將xixi,21分別封裝到people的name和age屬性中

self是一個形式參數,當執行p1 = people(‘xixi‘,21)時,self等於p1

同理p2 = people(‘hehe‘,23)時,self等於p2

所以,內容實際被封裝到了對象,p1,p2中,每個對象都有name和age屬性,都封裝到對象p1,2中

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

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

  • 通過對象直接調用
  • 通過self間接調用

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

對象 p1 和 p2 在存儲到內存中,根據保存格式可以如此調用被封裝的內容:對象.屬性名

技術分享圖片
 1 class people:
 2     def __init__(self,name,age):
 3         self.name = name
 4         self.age = age
 5 #根據類people創建對象
 6 #自動執行people類的__init__方法
 7 p1 = people(xixi,21)
 8 print(p1.name) #直接調用p1對象的name屬性
 9 print(p1.age)  #p1的age屬性
10 p2 = people(hehe,23)
11 print(p2.name)
12 print(p2.age)
View Code

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

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

技術分享圖片
 1 class people:
 2     def __init__(self,name,age):
 3         self.name = name
 4         self.age = age
 5 
 6     def attr(self):
 7         print(self.name)
 8         print(self.age)
 9 #根據類people創建對象
10 #自動執行people類的__init__方法
11 p1 = people(xixi,21)
12 p1.attr()
13 p2 = people(hehe,23)
14 p2.attr()
View Code

Python默認會將p1傳給self參數,即:p1.attr(p1),所以,此時方法內部的 self = p1,即:self.name 是 xixi ;self.age 是 12,同理,p2也是如此.

綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然後通過對象直接或者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()
面向對象編程

二、繼承

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

如:

男孩:talk,sleep,walk,eat

女孩:talk,sleep,walk,eat

技術分享圖片
class man:

    def talk(self):
        print 喵喵叫

    def eat(self):
        # do something

    def walk(self):
        # do something

    def slepp(self):
        # do something

  class woman:

    def talk(self):
        print 喵喵叫

    def eat(self):
        # do something

    def walk(self):
        # do something

    def slepp(self):
        # do something
偽代碼

上述代碼不難看出,talk,sleep,walk,eat是男和女都具有的功能,而我們卻分別的男和nv的類中編寫了兩次。如果使用 繼承 的思想,如下實現:

people:talk,sleep,walk,eat

man:喝酒

woman:穿裙子

技術分享圖片
class people:

    def eat(self):
        # do something

    def walk(self):
        # do something

    def eat(self):
        # do something

    def sleep(self):
        # do something

# 在類後面括號中寫入另外一個類名,表示當前類繼承另外一個類
class man(people):

    def 喝酒(self):
        print 喝酒
        
# 在類後面括號中寫入另外一個類名,表示當前類繼承另外一個類
class woman(people):

    def 穿裙子(self):
        print 穿裙子
偽代碼 技術分享圖片
class people:

    def eat(self):
        print "%s 吃 " %self.name

    def walk(self):
        print "%s 走 " %self.name

    def sleep(self):
        print "%s 睡 " %self.name

    def talk(self):
        print "%s 說 " %self.name


class man(people):

    def __init__(self, name):
        self.name = name
       

    def drink(self):
        print 喝酒

class woman(Animal):
    
    def __init__(self, name):
        self.name = name
        
        
    def wear(self):
        print 穿裙子
        

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

p1 = man(yu)
p1.eat()

p2 = man(li)
p2.drink()

p3 = Dog(liu)
p3.eat()
實例演示

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

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

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

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

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

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

技術分享圖片

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

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

經典類:

class p1:
     pass
class p2(p1):
     pass

新式類:

class p1(object):
      pass
class p2(p1):
      pass
技術分享圖片
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()
經典類多繼承 技術分享圖片
class D(object):

    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類中麽有,則繼續去C類中找,如果C類中麽有,則繼續去D類中找,如果還是未找到,則報錯
# 所以,查找順序:A --> B --> C --> D
# 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
a.bar()
新式類繼承

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

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

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

三、多態

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

“當看到一只鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麽這只鳥就可以被稱為鴨子。”

我們並不關心對象是什麽類型,到底是不是鴨子,只關心行為。

鴨子類型在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。

技術分享圖片
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) 
python‘鴨子類型‘

總結

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

  • 面向對象是一種編程方式,此編程方式的實現是基於對 對象 的使用
  • 類 是一個模板,模板中包裝了多個“函數”供使用
  • 對象,根據模板創建的實例(即:對象),實例用於調用被包裝在類中的函數
  • 面向對象三大特性:封裝、繼承和多態.

類和對象在內存中是如何保存?

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

技術分享圖片

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

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

  1. 根據當前對象中的 類對象指針 找到類中的方法
  2. 將對象 p1當作參數傳給 方法的第一個參數 self

python之面向對象編程一