Python的核心知識——面向對象

一、WHY 學習面向對象?

假設現在有一個場景是這樣的,我們要編寫一個遊戲,裏面有主角,NPC,怪物等等這些模型,那麼如果我們不學面向對象的話,我們就需要一個一個的編寫它們的屬性及動作,一般情況下,一個大型的遊戲比如LOL(這遊戲是真的火,都好幾年了還沒被幹掉)裏面的英雄,小兵,大龍,有好上百個模型吧。我們不可能一個一個的寫,那代碼量就不是一兩個g那麼大了(其實這就是面向過程的思想,POP)!仔細觀察會發現它們這些模型都有相同的屬性和動作,比如:都有生命值,魔法值等屬性,攻擊等動作,所以編程語言開發者們就在想了,有沒有一個東西把它們共同的屬性、動作封裝起來,這時候類概念應運而生了,就是我們這篇文章的主體對象,而這種思想就是面向對象(OOP)!

那什麼是面向對象編程呢?

度娘一下:面向對象(Object Oriented)是軟件開發方法。面向對象的概念和應用已超越了程序設計和軟件開發,擴展到如數據庫系統、交互式界面、應用結構、應用平臺、分佈式系統、網絡管理結構、CAD技術、人工智能等領域。面向對象是一種對現實世界理解和抽象的方法,是計算機編程技術發展到一定階段後的產物。

巴拉巴拉一堆,感覺懵懵懂懂,個人的理解就是,一種思想,相對於面向過程的一種思想。這是我個人對關於面向對象與面向過程的區別的理解:鏈接在此

二、類和對象

類和對象是面向對象編程裏面的兩個非常重要的概念。它們的關係嘛,嗯~,如果是說類是一張汽車的圖紙,那麼一輛根據這個圖紙製造出來的汽車就是一個對象!

1.類

類啥是類?用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。就可以理解爲之前我們學習的函數,是一種有自治系統的函數,有自己的屬性方法等等。類也是虛的,和函數一樣,只有當它被實例化時纔是計算機要運行的代碼塊,一個稿紙。

2.對象

對象又是啥?女朋友?男朋友?nonono,編程中的對象可不男女朋友萬能多了,它可以幫你製造一個男朋友或者女朋友,厲害吧!那啥是對象?對象就是類的實例化,上面說了類是虛的,紙上談兵,而對象是實,招兵買馬。和函數一樣,函數是虛的,調用函數是實的。

三、定義類和創建對象

那問題來了,知道它們的重要性,就得學會用它們了吧!那怎麼用?聽我慢慢道來!

首先這裏推薦一個適合工作用的Python編程工具(看個人吧!想用就用,畢竟好東西要一起分享嘛!):pycharm

注意:像這種官方工具,儘量使用專業版,首先同爲在IT行業的我們應該尊重編寫pycharm的團隊成果,其次在使用社區版的軟件是不全面,這時候我們就會想用盜版破解軟件,如果使用盜版軟件,在以後的工作中就會涉及到版權的法律問題,那就不是一兩百可以解決的問題了。

1.類的定義

格式:

class 類的名字:
…類的屬性 = 屬性值
…類的方法(self, [參數]):
… …方法體

類的屬性,在Python中類的屬性的定義與變量定義一樣,記得賦值
類的方法,在Python中類的方法的定義與普通的函數定義方法一樣,記得加self變量
代碼

class Str:
    #一般Python屬性的定義是直接賦值的,不像C++等編程語言是不可以賦值的
    name = "cc"
    age = 18
    #類的方法定義在參數表裏,必須加上一個變量,一般情況Python編程者普遍使用self爲變量名
    #self就是相對於C++的this指針,由於類是模型,而對象是由類實例化產生的,所以爲了區分不同的對象,我們通過引進self變量區別
    #self變量的意思是將實例化後的對象的名字傳遞給self,而這種傳遞是在類裏面的,調用方法時不需要傳遞
    #說白了,self就是一種標記實例化對象的全局變量,而方法裏面的變量是局部變量
    #__init__魔法方法就是對類的初始化,在定義時本身就有的,現在是把它進行覆蓋(後面會寫),是不需要在實例化時調用的
    #通常稱__init__爲構造方法
    #其實類裏面還有很多類似的方法,自行探索
    #self.name是對類內部屬性的訪問或者複製
    def __init__(self, Name):
    	self.name = Name
    def S_print(self):
        print("Name is {}, Age is {}".format(self.name, self.age))

類定義是虛的,不會在程序中顯示!

2.對象的創建

格式:

實例化對象名 = 類名([參數])
實例化對象名.類屬性名
實例化對象名.類方法名

代碼

class Str:
    name = "cc"
    age = 18
    def __init__(self, Name):
    	self.name = Name
    def S_print(self):
        print("Name is {}, Age is {}".format(self.name, self.age))

s = Str("qq")	#括號裏的參數是當__init__魔法方法的列表裏面有除了self屬性外,還有其他屬性的話,才添加的
print(s.name)
s.S_print()

結果顯示
在這裏插入圖片描述

四、面向對象三大特徵

Python之所以號稱面向對象編程語言,是因爲具有面向對象編程特有的三個特徵:封裝、繼承、多態。

類對象正是因爲有了這三大法寶才變得如此這般之厲害!

1.封裝

封裝顧名思義就是打包的意思,就是將類裏面的屬性和方法打包起來,放在類裏面成爲一個整體,這樣屬性和方法可以私有化了,並且提供了公共的方法去訪問和賦值類的屬性。

通過對類屬性的封裝使得類屬性更加穩定和安全,同時通過屬性私有化,還必須提供setter(設置方法)和getter(獲取方法) 兩種方法來讓外界訪問和修改屬性。

在Python中想要私有化類中的屬性和方法只要在屬性和方法前面添加兩個下劃線__就可以了(例如:__name),而單下劃線_也是一種私有的(例如:_name),但是這種下劃線屬於口頭私有,即意思是雖然可以被訪問,但是請把其視爲私有變量,不要隨意訪問(好坑)。

在Python中可以通過對象名.類屬性名/方法名進行訪問屬性和調用方法。

在Python中的私有屬性和方法是僞私有,就是有它們只換了一個名,在外部也可以進行訪問,只要在私有屬性前面加上單引號類名_類名__屬性名即可(例如:對象名._類名__屬性名,由於是訪問,所以加對象名)

Python有三種封裝的寫法

(1)直接私有化屬性,提供getter和setter
(2)直接私有化屬性,提供getter和setter,引入了property的全局函數
(3)使用property裝飾器完成封裝

a.直接私有化屬性,提供getter和setter

這種方法就是提供gettersetter兩種方法,比較常用的方法了。

格式

def 獲取屬性方法名(self):
…return self.__屬性名
def 設置屬性方法名(self, 用戶輸入的值):
self.__屬性名 = 用戶輸入的值

代碼

class Str:
    __name = "cc"
    _age = 18
    def __init__(self, Name):
    	self.__name = Name
    #提供name_getter方法來返回name值
    def name_getter(self):
    	return self.__name
    #提供name_setter方法來設置name值
    def name_setter(self, Name):
    	self.__name = Name
    def S_print(self):
        print("Name is {}, Age is {}".format(self.__name, self._age))
s = Str("qq")
print(s.name_getter())
s.name_setter("xx")
print(s.name_getter())
print(s._age)
print(s.__name)

結果顯示
在這裏插入圖片描述

由結果可以看出我們在__init__(構造方法)前面定義屬性是沒必要的,一般情況下,會在構造方法裏面定義屬性,並賦初值。

有兩個下劃線的屬性是不可以訪問的,有單下劃線的屬性可以訪問。

b.直接私有化屬性,提供getter和setter,引入了property的全局函數

這種方法是在上面的方法基礎上引入property全局函數,使得在訪問屬性時更加方便。

函數格式

class property([fget[, fset[, fdel[, doc]]]])

參數

fget – 獲取屬性值的函數
fset – 設置屬性值的函數
fdel – 刪除屬性值函數
doc – 屬性描述信息

方法格式

def 獲取屬性方法名(self):
…return self.__屬性名
def 設置屬性方法名(self, 用戶輸入的值):
self.__屬性名 = 用戶輸入的值
變量名(推薦屬性名,就相當於設置了一個屬性)=property(設置屬性方法名, 獲取屬性方法名)

注意:設置屬性方法名在前面,獲取方法名在後面

代碼

class Str:
    def __init__(self, Name):
    	self.__name = Name
    #提供name_getter方法來返回name值
    def name_getter(self):
    	return self.__name
    #提供name_setter方法來設置name值
    def name_setter(self, Name):
    	self.__name = Name

    name = property(name_getter, name_setter)    
    def S_print(self):
        print("Name is {}, Age is {}".format(self.__name, self._age))
s = Str("qq")
print(s.name)
s.name = "xx"
print(s.name)

結果顯示
在這裏插入圖片描述

c.使用property裝飾器完成封裝

上面的調用方法又略顯複雜,這對於追求完美的Python程序員來說,是不太滿意的!所以就出現了第三種方法。這種方法是通過@property裝飾器負責把一個方法變成只讀屬性調用。

把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又創建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值,於是,我們就擁有一個可控的屬性操作。

@property廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減少了出錯的可能性。

格式

@property
def 屬性方法名(self):
return self.__屬性名
@屬性方法名.setter
def 屬性方法名(self, 用戶輸入的值):
self.__屬性名 = 用戶輸入的值

代碼

class Str:
    def __init__(self, Name):
    	self.__name = Name
        
    @property
    def S_name(self):
        return self.__name

    @S_name.setter
    def S_name(self, Name):
        self.__name = Name

   
    def S_print(self):
        print("Name is {}, Age is {}".format(self.__name, self._age))
s = Str("qq")
s.name = "xx"
print(s.name)

結果顯示
在這裏插入圖片描述
注意:在使用第三種封裝方法時@方法名.setter中必須是setter,而且由於是配對的,所以只有先設置纔可以調用。

2.繼承

繼承嘛!就是繼承父輩的遺產或者知識類似,模擬人類的繼承這種方式,子代繼承父代的財富或知識。由於面向對象是模擬人類的實現,所以需要有繼承關係,即類與類之間的繼承關係。其實在Python3.x版本中所有的自定義類都繼承object類,但是Python2.x中卻不是,Python和C++一樣還支持多繼承。

當一個類繼承其他類(也就是父類,也可以叫超類),那麼它就擁有了繼承父類的一些方法和屬性。如果我們繼承了父類的方法和屬性,如果這些方法和屬性滿足我們使用,直接調用就行了。但是如果某個方法或者屬性不滿足我們的需求可以通過重寫(覆蓋)代碼。

注意:父類的公開方法和屬性可以繼承,私有方法和屬性是不能被繼承,但是嘛!上面說了Python是僞私有,所以還是可以通過非正常手段繼承父類私有方法的。

super繼承兩種方法,父類有屬性加變量名

a.正常繼承

格式

class 子類(父類):
…類的屬性 = 屬性值
…類的方法(self, [參數]):
… …方法體
#上面那塊代碼塊和之前定義類的形式一樣了,就是看你繼不繼承父類屬性和方法了

代碼

#父類
class People:
    def __init__(self):
        print("我是父類")
#子類1
class Employee(People):
    pass
#子類2
class Teacher(People):
    def __init__(self):
        print("我是子類2")

P =  People()
E =  Employee()
T = Teacher()

顯示結果
在這裏插入圖片描述
由結果可以看出一個父類可以有多個子類,就像一個父親可以有多個孩子一樣!

b.非法繼承

一種方法是通過父類名.方法名(self),這種方法是非綁定,另一種是調用super函數。這種方法是綁定。
格式

父類名.方法名(self)
super().init()

代碼

#父類
class People:
    def __init__(self):
        self.__name = "xx"
        print("我是父類")
    def P_print(self):
        print("我是{}".format(self.__name))
#子類1
class Employee(People):
    def __init__(self):
        People.__init__(self)
        print("我是子類1\n")

#子類2
class Teacher(People):
    def __init__(self):
        super().__init__()
        print("我是子類2\n")

P =  People()
E =  Employee()
T = Teacher()
P.P_print()
E.P_print()
T.P_print()

顯示結果
在這裏插入圖片描述

c.多繼承

多繼承的意思就是一個徒弟有多個師傅傳授武功。Python中也一樣一個子類可以有多個父類,但是不推薦,這種方法很容易使得代碼顯得複雜,而且容易拋出異常。

格式

class 子類名(父類1, 父類2,…):

代碼

#父類1
class People:
    def P_print(self):
        print("我是父類1")
#父類2
class Employee:
    def E_print(self):
        print("我是父類2")

#子類
class Teacher(People, Employee):
    pass
P =  People()
E =  Employee()
T = Teacher()
P.P_print()
E.E_print()
T.P_print()
T.E_print()

結果顯示
在這裏插入圖片描述

d.Python沒有函數的重載,但是有重寫(或者叫覆蓋)

重載就是函數名稱相同,函數參數個數、參數類型和返回值可以不同的兩個或者兩個以上的函數,當我們調用的時候,會自動的執行對應的函數。

而重寫就是函數名稱,函數參數個數、參數類型和返回值都相同的兩個函數或者以上的函數。

由於Python語言本身沒有數據類型這一說,加上是腳本語言,變量會隨着賦值的改變而改變,所以Python是沒有函數重載的,但是Python是支持重寫的。

重寫格式

def 父類的方法([參數]):
…方法體
def 與父類相同名字的子類方法([相同參數]):
…不同方法體

代碼

#父類1
class People:
    def P_print(self):
        print("我是父類")

#子類
class Teacher(People):
    def P_print(self):
        print("我是子類")

P =  People()
T = Teacher()
P.P_print()
T.P_print()

結果顯示
在這裏插入圖片描述

3.多態

多態是在繼承的基礎上,父類引用指向子類實例(對下個)的現象,就是說父類可以調用子類方法!!!

在Python這些弱類型語音,天生支持多態。

爲什麼呢?

因爲Python是在定義變量、方法名或者類的時候是沒有數據類型怎麼一說的。首先我們看看C++的多態,假設AList繼承List這個父類,那麼我們就可以定義:

List a = new AList()

而在Python中是沒有數據類型在變量前面修飾的,就像下面:

a = AList()

所以在Python這些弱類型語音,天生支持多態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章