Day28:面向對象編程——類和對象
一、面向對象的程序設計
在此之前用到的事面向過程的程序設計,它的核心是過程(流水線式思維),過程即解決問題的步驟,面向過程的設計就好比設計好一條流水線,考慮周全什麽時候處理什麽東西。
優點:極大的降低了程序的復雜度
缺點是:一套流水線或者流程就是用來解決一個問題,生產汽水的流水線無法生產汽車,即便是能,也得是大改,改一個組件,牽一發而動全身。
應用場景:一旦完成基本很少改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向對象的程序設計的核心是對象。程序中的對象有自己的屬性。
優點是:解決了程序的擴展性。對某一個對象單獨修改,會立刻反映到整個體系中,如對遊戲中一個人物參數的特征和技能修改都很容易。
缺點:可控性差,無法向面向過程的程序設計流水線式的可以很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題,即便是上帝也無法預測最終結果。於是我們經常看到一個遊戲人某一參數的修改極有可能導致陰霸的技能出現,一刀砍死3個人,這個遊戲就失去平衡。
應用場景:需求經常變化的軟件,一般需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方。
面向對象的程序設計並不是全部,對於一個軟件質量來說,面向對象的程序設計只是用來解決擴展性。
二、類和對象
2.1什麽是對象,什麽是類
提示:python的class術語與c++有一定區別,與 Modula-3更像。
python中一切皆為對象,且python3統一了類與類型的概念,類型就是類,所以,不管你信不信,你已經使用了很長時間的類了。
>>> dict #類型dict就是類dict <class ‘dict‘> >>> d=dict(name=‘egon‘) #實例化 >>> d.pop(‘name‘) #向d發一條消息,執行d的方法pop ‘egon‘
基於面向對象設計一個款遊戲:英雄聯盟,每個玩家選一個英雄,每個英雄都有自己的特征和和技能,特征即數據屬性,技能即方法屬性,特征與技能的結合體就是一個對象。
從一組對象中提取相似的部分就是類,類所有對象都具有的特征和技能的結合體
在python中,用變量表示特征,用函數表示技能,因而類是變量與函數的結合體,對象是變量與方法(指向類的函數)的結合體
補充幾個有意思的點:
garen_hero.Q()稱為向garen_hero這個對象發送了一條消息,讓他去執行Q這個函數,完成一個功能,類似的有:
garen_hero.W()
garen_hero.E()
garen_hero.R()
一個英雄可以攻擊另外一個英雄,這就是對象之間的交互
garen_hero.attack(Riven)
2.2類相關知識
2.21 初試類
在python中聲明函數與聲明類很相似
聲明函數
def functionName(args): ‘函數文檔字符串‘ 函數體
聲明類
‘‘‘ class 類名: ‘類的文檔字符串‘ 類體 ‘‘‘ #我們創建一個類 class Data: pass
大前提: 1.只有在python2中才分新式類和經典類,python3中統一都是新式類 2.新式類和經典類聲明的最大不同在於,所有新式類必須繼承至少一個父類 3.所有類甭管是否顯式聲明父類,都有一個默認繼承object父類(講繼承時會講,先記住) 在python2中的區分 經典類: class 類名: pass 新式類: class 類名(父類): pass 在python3中,上述兩種定義方式全都是新式類
在本節開頭介紹得出結論,類是數據與函數的結合,二者稱為類的屬性
class Garen: #定義英雄蓋倫的類,不同的玩家可以用它實例出自己英雄; camp=‘Demacia‘ #所有玩家的英雄(蓋倫)的陣營都是Demacia; def attack(self,enemy): #普通攻擊技能,enemy是敵人; enemy.life_value-=self.aggressivity #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值
2.2.2 類有兩種作用:屬性引用和實例化
2.2.2.1 屬性引用(類名.屬性)
>>> Garen.camp #引用類的數據屬性,該屬性與所有對象/實例共享 ‘Demacia‘ >>> Garen.attack #引用類的函數屬性,該屬性也共享 <function Garen.attack at 0x101356510> >>> Garen.name=‘Garen‘ #增加屬性 >>> del Garen.name #刪除屬性
2.2.2.2 實例化(__init__與self)
類名加括號就是實例化,會自動觸發__init__函數的運行,可以用它來為每個實例定制自己的特征。
class Garen: #定義英雄蓋倫的類,不同的玩家可以用它實例出自己英雄; camp=‘Demacia‘ #所有玩家的英雄(蓋倫)的陣營都是Demacia; def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻擊力58...; self.nickname=nickname #為自己的蓋倫起個別名; self.aggressivity=aggressivity #英雄都有自己的攻擊力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻擊技能,enemy是敵人; enemy.life_value-=self.aggressivity #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值。
實例化:類名+括號
>>> g1=Garen(‘草叢倫‘) #就是在執行Garen.__init__(g1,‘草叢倫‘),然後執行__init__內的代碼g1.nickname=‘草叢倫’等
self的作用是在實例化時自動將對象/實例本身傳給__init__的第一個參數,self可以是任意名字,但是瞎幾把寫別人就看不懂了。
這種自動傳遞的機制還體現在g1.attack的調用上,後續會介紹。
一:我們定義的類的屬性到底存到哪裏了?有兩種方式查看 dir(類名):查出的是一個名字列表 類名.__dict__:查出的是一個字典,key為屬性名,value為屬性值 二:特殊的類屬性 類名.__name__# 類的名字(字符串) 類名.__doc__# 類的文檔字符串 類名.__base__# 類的第一個父類(在講繼承時會講) 類名.__bases__# 類所有父類構成的元組(在講繼承時會講) 類名.__dict__# 類的字典屬性 類名.__module__# 類定義所在的模塊 類名.__class__# 實例對應的類(僅新式類中)
2.3對象相關知識
對象是關於類而實際存在的一個例子,即實例。
>>> g1=Garen(‘草叢倫‘) #類實例化得到g1這個實例,基於該實例我們講解實例相關知識 >>> type(g1) #查看g1的類型就是類Garen <class ‘__main__.Garen‘> >>> isinstance(g1,Garen) #g1就是Garen的實例 True
2.3.1 對象/實例只有一種作用:屬性引用
#對象/實例本身其實只有數據屬性 >>> g1.nickname ‘草叢倫‘ >>> g1.aggressivity >>> g1.life_value ‘‘‘ 查看實例屬性 同樣是dir和內置__dict__兩種方式 特殊實例屬性 __class__ __dict__ .... ‘‘‘
對象/實例本身只有數據屬性,但是python的class機制會將類的函數綁定到對象上,稱為對象的方法,或者叫綁定方法,綁定方法唯一綁定一個對象,同一個類的方法綁定到不同的對象上,屬於不同的方法,內存地址都不會一樣。
>>> g1.attack #對象的綁定方法 <bound method Garen.attack of <__main__.Garen object at 0x101348dd8>> >>> Garen.attack #對象的綁定方法attack本質就是調用類的函數attack的功能,二者是一種綁定關系 <function Garen.attack at 0x101356620>
對象的綁定方法的特別之處在於:obj.func()會把obj傳給func的第一個參數。
2.4 對象之間的交互
我們可以仿照garen類再創建一個Riven類
class Riven: camp=‘Noxus‘ #所有玩家的英雄(銳雯)的陣營都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻擊力54; self.nickname=nickname #為自己的銳雯起個別名; self.aggressivity=aggressivity #英雄都有自己的攻擊力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻擊技能,enemy是敵人; enemy.life_value-=self.aggressivity #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值。
實例出一個Riven來
>>> r1=Riven(‘銳雯雯‘)
交互:銳雯雯攻擊草叢倫,反之一樣
>>> g1.life_value 455 >>> r1.attack(g1) >>> g1.life_value 401
2.5 類名稱空間與對象/實例名稱空間
創建一個類就會創建一個類的名稱空間,用來存儲類中定義的所有名字,這些名字稱為類的屬性。
而類有兩種屬性:數據屬性和函數屬性
其中類的數據屬性是共享給所有對象的。
>>> id(r1.camp) #本質就是在引用類的camp屬性,二者id一樣 4315241024 >>> id(Riven.camp) 4315241024
而類的函數屬性是綁定到所有對象的:
>>> id(r1.attack) >>> id(Riven.attack) ‘‘‘ r1.attack就是在執行Riven.attack的功能,python的class機制會將Riven的函數屬性attack綁定給r1,r1相當於拿到了一個指針,指向Riven類的attack功能 除此之外r1.attack()會將r1傳給attack的第一個參數 ‘‘‘
創建一個對象/實例就會創建一個對象/實例的名稱空間,存放對象/實例的名字,稱為對象/實例的屬性
在obj.name會先從obj自己的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常 。
Day28:面向對象編程——類和對象