1. 程式人生 > 實用技巧 >python基礎-面向物件程式設計

python基礎-面向物件程式設計

一 物件的概念

面向過程:
核心是"過程"二字

過程的終極奧義就是將程式流程化
過程是"流水線",用來分步驟解決問題的

面向物件:
核心是"物件"二字
物件的終極奧義就是將程式"整合"
物件是"容器",用來盛放資料與功能的

類也是"容器",該容器用來存放同類物件共有的資料與功能

”面向物件“的核心是“物件”二字,而物件的精髓在於“整合“,什麼意思?

所有的程式都是由”資料”與“功能“組成,因而編寫程式的本質就是定義出一系列的資料,然後定義出一系列的功能來對資料進行操作。在學習”物件“之前,程式中的資料與功能是分離開的,如下

#
資料:name、age、sex name='lili' age=18 sex='female' # 功能:tell_info def tell_info(name,age,sex): print('<%s:%s:%s>' %(name,age,sex)) # 此時若想執行檢視個人資訊的功能,需要同時拿來兩樣東西,一類是功能tell_info,另外一類則是多個數據name、age、sex,然後才能執行,非常麻煩 tell_info(name,age,sex)
# 學生的功能
def tell_stu_info(stu_obj):
    print('學生資訊:名字:%s 年齡:%s 性別:%s
' % ( stu_obj['stu_name'], stu_obj['stu_age'], stu_obj['stu_gender'] )) def set_info(stu_obj, x, y, z): stu_obj['stu_name'] = x stu_obj['stu_age'] = y stu_obj['stu_gender'] = z stu_obj = { 'stu_school': 'oldboy', 'stu_name': 'egon', 'stu_age': 18,
'stu_gender': 'male', 'tell_stu_info': tell_stu_info, 'set_info': set_info } stu1_obj = { 'stu_school': 'oldboy', 'stu_name': 'lili', 'stu_age': 19, 'stu_gender': 'female', 'tell_stu_info': tell_stu_info, 'set_info': set_info } # 課程的資料 course_name = 'python' course_period = '6mons' course_score = 10 # 課程的功能 def tell_coure_info(): print('課程資訊:名字:%s 週期:%s 學分:%s' % (course_name, course_period, course_score))

在學習了“物件”之後,我們就有了一個容器,該容器可以盛放資料與功能,所以我們可以說:物件是把資料與功能整合到一起的產物,或者說”物件“就是一個盛放資料與功能的容器/箱子/盒子。

如果把”資料“比喻為”睫毛膏“、”眼影“、”脣彩“等化妝所需要的原材料;把”功能“比喻為眼線筆、眉筆等化妝所需要的工具,那麼”物件“就是一個彩妝盒,彩妝盒可以把”原材料“與”工具“都裝到一起

如果我們把”化妝“比喻為要執行的業務邏輯,此時只需要拿來一樣東西即可,那就是彩妝盒,因為彩妝盒裡整合了化妝所需的所有原材料與功能,這比起你分別拿來原材料與功能才能執行,要方便的多。

​ 在瞭解了物件的基本概念之後,理解面向物件的程式設計方式就相對簡單很多了,面向物件程式設計就是要造出一個個的物件,把原本分散開的相關資料與功能整合到一個個的物件裡,這麼做既方便使用,也可以提高程式的解耦合程度,進而提升了程式的可擴充套件性(需要強調的是,軟體質量屬性包含很多方面,面向物件解決的僅僅只是擴充套件性問題)

二 類與物件

類即類別/種類,是面向物件分析和設計的基石,如果多個物件有相似的資料與功能,那麼該多個物件就屬於同一種類。有了類的好處是:我們可以把同一類物件相同的資料與功能存放到類裡,而無需每個物件都重複存一份,這樣每個物件裡只需存自己獨有的資料即可,極大地節省了空間。所以,如果說物件是用來存放資料與功能的容器,那麼類則是用來存放多個物件相同的資料與功能的容器。

​ 綜上所述,雖然我們是先介紹物件後介紹類,但是需要強調的是:在程式中,必須要事先定義類,然後再呼叫類產生物件(呼叫類拿到的返回值就是物件)。產生物件的類與物件之間存在關聯,這種關聯指的是:物件可以訪問到類中共有的資料與功能,所以類中的內容仍然是屬於物件的,類只不過是一種節省空間、減少程式碼冗餘的機制,面向物件程式設計最終的核心仍然是去使用物件。

​ 在瞭解了類與物件這兩大核心概念之後,我們就可以來介紹一下面向物件程式設計啦。

三 面向物件程式設計

3.1 類的定義與例項化

我們以開發一個清華大學的選課系統為例,來簡單介紹基於面向物件的思想如何編寫程式

面向物件的基本思路就是把程式中要用到的、相關聯的資料與功能整合到物件裡,然後再去使用,但程式中要用到的資料以及功能那麼多,如何找到相關連的呢?我需要先提取選課系統裡的角色:學生、老師、課程等,然後顯而易見的是:學生有學生相關的資料於功能,老師有老師相關的資料與功能,我們單以學生為例,

# 學生的資料有
學校
名字
年齡
性別

# 學生的功能有
選課

詳細的

# 學生1:
    資料:
        學校=清華大學
        姓名=李建剛
        性別=年齡=28
    功能:
        選課

# 學生2:
    資料:
        學校=清華大學
        姓名=王大力
        性別=年齡=18
    功能:
        選課

# 學生3:
    資料:
        學校=清華大學
        姓名=牛嗷嗷
        性別=年齡=38
    功能:
        選課

我們可以總結出一個學生類,用來存放學生們相同的資料與功能

# 學生類
    相同的特徵:
        學校=清華大學
    相同的功能:
        選課

基於上述分析的結果,我們接下來需要做的就是在程式中定義出類,然後呼叫類產生物件

class Student: # 類的命名應該使用“駝峰體”

    school='清華大學' # 資料

    def choose(self): # 功能
        print('%s is choosing a course' %self.name)

類體最常見的是變數的定義和函式的定義,但其實類體可以包含任意Python程式碼,類體的程式碼在類定義階段就會執行,因而會產生新的名稱空間用來存放類中定義的名字,可以列印Student.__dict__來檢視類這個容器內盛放的東西

>>> print(Student.__dict__)
{..., 'school': '清華大學', 'choose': <function Student.choose at 0x1018a2950>, ...}

呼叫類的過程稱為將類例項化,拿到的返回值就是程式中的物件,或稱為一個例項

>>> stu1=Student() # 每例項化一次Student類就得到一個學生物件
>>> stu2=Student()
>>> stu3=Student()

如此stu1、stu2、stu3全都一樣了(只有類中共有的內容,而沒有各自獨有的資料),想在例項化的過程中就為三位學生定製各自獨有的資料:姓名,性別,年齡,需要我們在類內部新增一個__init__方法,如下

class Student:
    school='清華大學'

    #該方法會在物件產生之後自動執行,專門為物件進行初始化操作,可以有任意程式碼,但一定不能返回非None的值
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age

    def choose(self): 
        print('%s is choosing a course' %self.name) 

然後我們重新例項出三位學生

>>> stu1=Student('李建剛','',28)
>>> stu2=Student('王大力','',18)
>>> stu3=Student('牛嗷嗷','',38)

單拿stu1的產生過程來分析,呼叫類會先產生一個空物件stu1,然後將stu1連同呼叫類時括號內的引數一起傳給Student.__init__(stu1,’李建剛’,’男’,28)

def __init__(self, name, sex, age):
    self.name = name  # stu1.name = '李建剛'
    self.sex = sex    # stu1.sex = '男'
    self.age = age    # stu1.age = 28

會產生物件的名稱空間,同樣可以用__dict__檢視

>>> stu1.__dict__
{'name': '李建剛', 'sex': '', 'age': 28}

至此,我們造出了三個物件與一個類,物件存放各自獨有的資料,類中存放物件們共有的內容

# 注意:類體程式碼是在類定義階段就會立即執行,會產生類的名稱空間
class Student:
    # 1、變數的定義
    stu_school='oldboy'

    # 2、功能的定義
    def tell_stu_info(stu_obj):
        print('學生資訊:名字:%s 年齡:%s 性別:%s' %(
            stu_obj['stu_name'],
            stu_obj['stu_age'],
            stu_obj['stu_gender']
        ))

    def set_info(stu_obj,x,y,z):
        stu_obj['stu_name']=x
        stu_obj['stu_age']=y
        stu_obj['stu_gender']=z

    # print('========>')

# print(Student.__dict__)

# 屬性訪問的語法
# 1、訪問資料屬性
# print(Student.stu_school) # Student.__dict__['stu_school']
# 2、訪問函式屬性
# print(Student.set_info) # Student.__dict__['set_info']

# Student.x=1111 #Student.__dict__['x]=111
# print(Student.__dict__)

# 二:再呼叫類產生物件
stu1_obj=Student()
stu2_obj=Student()
stu3_obj=Student()

# print(stu1_obj.__dict__)
# print(stu2_obj.__dict__)
# print(stu3_obj.__dict__)

# 為物件定製自己獨有的屬性
# 問題1:程式碼重複
# 問題2:屬性的查詢順序
# stu1_obj.stu_name='egon'   # stu1_obj.__dict__['stu_name']='egon'
# stu1_obj.stu_age=18        # stu1_obj.__dict__['stu_age']=18
# stu1_obj.stu_gender='male' #  stu1_obj.__dict__['stu_gender']='male'
# # print(stu1_obj.__dict__)
#
# stu2_obj.stu_name='lili'
# stu2_obj.stu_age=19
# stu2_obj.stu_gender='female'
# # print(stu2_obj.__dict__)
#
# stu3_obj.stu_name='jack'
# stu3_obj.stu_age=20
# stu3_obj.stu_gender='male'
# # print(stu2_obj.__dict__)

# 解決問題一:
# 解決方案一:
# def init(obj,x,y,z):
#     obj.stu_name=x
#     obj.stu_age=y
#     obj.stu_gender=z
#
# init(stu1_obj,'egon',18,'male')
# init(stu2_obj,'lili',19,'female')
# init(stu2_obj,'jack',20,'male')

# 解決方案二:
# 一:先定義類
class Student:
    # 1、變數的定義
    stu_school='oldboy'

    # 空物件,'egon',18,'male'
    def __init__(obj,x,y,z):
        obj.stu_name=x # 空物件.stu_name='egon'
        obj.stu_age=y  # 空物件.stu_age=18
        obj.stu_gender=z # 空物件.stu_gender='male'
        # return None

    # 2、功能的定義
    def tell_stu_info(stu_obj):
        print('學生資訊:名字:%s 年齡:%s 性別:%s' %(
            stu_obj['stu_name'],
            stu_obj['stu_age'],
            stu_obj['stu_gender']
        ))

    def set_info(stu_obj,x,y,z):
        stu_obj['stu_name']=x
        stu_obj['stu_age']=y
        stu_obj['stu_gender']=z

    # print('========>')

# 二:再呼叫類產生物件
# 呼叫類的過程又稱之為例項化,發生了三件事
# 1、先產生一個空物件
# 2、python會自動呼叫類中的__init__方法然將空物件已經呼叫類時括號內傳入的引數一同傳給__init__方法
# 3、返回初始完的物件
stu1_obj=Student('egon',18,'male') # Student.__init__(空物件,'egon',18,'male')
# stu2_obj=Student('lili',19,'female')
# stu3_obj=Student('jack',20,'male')

# print(stu1_obj.__dict__)
# print(stu2_obj.__dict__)
# print(stu3_obj.__dict__)


# 總結__init__方法
# 1、會在呼叫類時自動觸發執行,用來為物件初始化自己獨有的資料
# 2、__init__內應該存放是為物件初始化屬性的功能,但是是可以存放任意其他程式碼,想要在類呼叫時就立刻執行的程式碼都可以放到該方法內
# 3、__init__方法必須返回None

3.2 屬性訪問

3.2.1 類屬性與物件屬性

在類中定義的名字,都是類的屬性,細說的話,類有兩種屬性:資料屬性和函式屬性,可以通過__dict__訪問屬性的值,比如Student.__dict__[‘school’],但Python提供了專門的屬性訪問語法

>>> Student.school # 訪問資料屬性,等同於Student.__dict__['school']
'清華大學'
>>> Student.choose # 訪問函式屬性,等同於Student.__dict__['choose']
<function Student.choose at 0x1018a2950>
# 除了檢視屬性外,我們還可以使用Student.attrib=value(修改或新增屬性),用del Student.attrib刪除屬性。

操作物件的屬性也是一樣

>>> stu1.name # 檢視,等同於obj1.__dict__[‘name']
'李建剛'
>>> stu1.course=’python’ # 新增,等同於obj1.__dict__[‘course']='python'
>>> stu1.age=38 # 修改,等同於obj1.__dict__[‘age']=38
>>> del obj1.course # 刪除,等同於del obj1.__dict__['course']

3.2.2 屬性查詢順序與繫結方法

物件的名稱空間裡只存放著物件獨有的屬性,而物件們相似的屬性是存放於類中的。物件在訪問屬性時,會優先從物件本身的__dict__中查詢,未找到,則去類的__dict__中查詢

1、類中定義的變數是類的資料屬性,是共享給所有物件用的,指向相同的記憶體地址

# id都一樣
print(id(Student.school)) # 4301108704

print(id(stu1.school)) # 4301108704
print(id(stu2.school)) # 4301108704
print(id(stu3.school)) # 4301108704

2、類中定義的函式是類的函式屬性,類可以使用,但必須遵循函式的引數規則,有幾個引數需要傳幾個引數

Student.choose(stu1) # 李建剛 is choosing a course
Student.choose(stu2) # 王大力 is choosing a course
Student.choose(stu3) # 牛嗷嗷 is choosing a course

但其實類中定義的函式主要是給物件使用的,而且是繫結給物件的,雖然所有物件指向的都是相同的功能,但是繫結到不同的物件就是不同的繫結方法,記憶體地址各不相同

print(id(Student.choose)) # 4335426280

print(id(stu1.choose)) # 4300433608
print(id(stu2.choose)) # 4300433608
print(id(stu3.choose)) # 4300433608

繫結到物件的方法特殊之處在於,繫結給誰就應該由誰來呼叫,誰來呼叫,就會將’誰’本身當做第一個引數自動傳入(方法__init__也是一樣的道理)

stu1.choose()  # 等同於Student.choose(stu1)
stu2.choose()  # 等同於Student.choose(stu2)
stu3.choose()  # 等同於Student.choose(stu3)

繫結到不同物件的choose技能,雖然都是選課,但李建剛選的課,不會選給王大力,這正是”繫結“二字的精髓所在。

#注意:繫結到物件方法的這種自動傳值的特徵,決定了在類中定義的函式都要預設寫一個引數self,self可以是任意名字,但命名為self是約定俗成的。

Python中一切皆為物件,且Python3中類與型別是一個概念,因而繫結方法我們早就接觸過

#型別list就是類
>>> list
<class 'list'>

#例項化的到3個物件l1,l2,l3
>>> l1=list([1,2,3])
>>> l2=list(['a','b','c'])
>>> l3=list(['x','y'])

#三個物件都有繫結方法append,是相同的功能,但記憶體地址不同
>>> l1.append
<built-in method append of list object at 0x10b482b48>
>>> l2.append
<built-in method append of list object at 0x10b482b88>
>>> l3.append
<built-in method append of list object at 0x10b482bc8>

#操作繫結方法l1.append(4),就是在往l1新增4,絕對不會將4新增到l2或l3
>>> l1.append(4) #等同於list.append(l1,4)
>>> l1
[1,2,3,4]
>>> l2
['a','b','c']
>>> l3
['x','y']

3.3.3 小結

在上述介紹類與物件的使用過程中,我們更多的是站在底層原理的角度去介紹類與物件之間的關聯關係,如果只是站在使用的角度,我們無需考慮語法“物件.屬性"中”屬性“到底源自於哪裡,只需要知道是通過物件獲取到的就可以了,所以說,物件是一個高度整合的產物,有了物件,我們只需要使用”物件.xxx“的語法就可以得到跟這個物件相關的所有資料與功能,十分方便且解耦合程度極高。