一、面向對象的程序設計的由來
點擊查看:http://www.cnblogs.com/hwlong/p/8855776.html
二、什么是面向對象的程序設計及為什么要有它
-
面向過程編程
面向過程的程序設計:核心是過程二字,過程指的是解決問題的步驟,即先干什么再干什么....面向過程的設計就好比精心設計好的一條流水線,是一種機械式的思維方式問題。
優點是:復雜度的問題流程化,進而簡單化(一個復雜的問題,分成一個個小的步驟去實現,實現小的步驟將會非常簡單)
缺點是:一套流水線或者流程就是用來解決一個問題,生產汽水的流水線無法生產汽車,即便是能,也得是大改,改一個組件,牽一發而動全身。
應用場景:一旦完成基本很少改變的場景,著名的例子有:Linux內核,git,以及Apache HTTP Server等。
-
面向對象編程
面向對象的程序設計:核心是對象二字,(要理解對象為何物,必須把自己當成上帝,上帝眼里世間存在的萬物皆為對象,不存在的也可以創造出來。面向對象的程序設計好比如來設計西游記,
如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題需要四個人:唐僧,沙和尚,豬八戒,孫悟空,每個人都有各自的特征和技能(這就是對象的概念,特征和技能分別對應對象
的數據屬性和方法屬性),然而這並不好玩,於是如來又安排了一群妖魔鬼怪,為了防止師徒四人在取經路上被搞死,又安排了一群神仙保駕護航,這些都是對象。然后取經開始,師徒四人與
妖魔鬼怪神仙交互着直到最后取得真經。如來根本不會管師徒四人按照什么流程去取),對象是特征與技能的結合體,基於面向對象設計程序就好比在創造一個世界,你就是這個世界的上帝,存
在的皆為對象,不存在的也可以創造出來,與面向過程機械式的思維方式形成鮮明對比,面向對象更加注重對現實世界的模擬,是一種“上帝式”的思維方式。
-
面向對象編程的優點:
優點是:解決了程序的擴展性。對某一個對象單獨修改,會立即反映到整個體系中,如對游戲中一個人物參數的特征和技能修改都很容易。
-
面向對象編程的缺點:
缺點: 1、編程的復雜度遠高於面向過程,不了解面向對象而立即上手基於它設計程序,極容易出現過度設計的問題。一些擴展性要求低的場景使用面向對象會徒增編程的難度,比如管理Linux系統的shell腳本
就不適合面向對象去設計,面向過程反而更加適合。 2、無法面向過程的程序設計流水式的可以很精准的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題,即便是上帝也無法精准地預測最終結果。於是我們經常看到對戰類
的游戲,新增一個游戲人物,在對戰的過程中極容易出現陰霸的技能,一刀砍死3個人,這種情況是無法准確預知的,只有對象之間交互才能准確地直到最終的結果。
-
面向對象編程的應用場景:
應用場景:需求經常變化的軟件,一般需求的變化都集中在用戶層,互聯網應用,企業內部軟件,游戲等都是面向對象的程序設計大顯身手的好地方
面向對象的程序設計並不是全部。對於一個軟件質量來說,面向對象的程序設計只是用來解決擴展性。

三、類與對象
類即類別、種類,是面向對象設計最重要的概念,對象是特征與技能的結合體,而類則是一系列對象相似的特征與技能的結合體
那么問題來了,先有的一個個具體的存在對象(比如一個具體存在的人),還是先有的人類這個概念,這個問題需要分兩種情況去看
在現實世界中:先有對象,再有類
世界上肯定是先出現各種各樣的實際存在的物體,然后隨着人類文明的發展,人類站在不同的角度總結出了不同的種類,如人類、動物類、植物類等概念
也就說,對象是具體的存在,而類僅僅只是一個概念,並不真實存在
在程序中:務必保證先定義類,后產生對象
這與函數的使用是類似的,先定義函數,后調用函數,類也是一樣的,在程序中需要先定義類,后調用類
不一樣的是,調用函數會執行函數體代碼返回的是函數體執行的結果,而調用類會產生對象,返回的是對象
按照上述步驟,我們來定義一個類
#在現實世界中,站在老男孩學校的角度:先有對象,再有類 對象1:李坦克 特征: 學校=oldboy 姓名=李坦克 性別=男 年齡=18 技能: 學習 吃飯 睡覺 對象2:王大炮 特征: 學校=oldboy 姓名=王大炮 性別=女 年齡=38 技能: 學習 吃飯 睡覺 對象3:牛榴彈 特征: 學校=oldboy 姓名=牛榴彈 性別=男 年齡=78 技能: 學習 吃飯 睡覺 現實中的老男孩學生類 相似的特征: 學校=oldboy 相似的技能: 學習 吃飯 睡覺
#在程序中,務必保證:先定義(類),后使用(產生對象) PS: 1. 在程序中特征用變量標識,技能用函數標識 2. 因而類中最常見的無非是:變量和函數的定義 #程序中的類 class OldboyStudent: school='oldboy' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') #注意: 1.類中可以有任意python代碼,這些代碼在類定義階段便會執行 2.因而會產生新的名稱空間,用來存放類的變量名與函數名,可以通過OldboyStudent.__dict__查看 3.對於經典類來說我們可以通過該字典操作類名稱空間的名字(新式類有限制),但python為我們提供專門的.語法 4.點是訪問屬性的語法,類中定義的名字,都是類的屬性 #程序中類的用法 .:專門用來訪問屬性,本質操作的就是__dict__ OldboyStudent.school #等於經典類的操作OldboyStudent.__dict__['school'] OldboyStudent.school='Oldboy' #等於經典類的操作OldboyStudent.__dict__['school']='Oldboy' OldboyStudent.x=1 #等於經典類的操作OldboyStudent.__dict__['x']=1 del OldboyStudent.x #等於經典類的操作OldboyStudent.__dict__.pop('x') #程序中的對象 #調用類,或稱為實例化,得到對象 s1=OldboyStudent() s2=OldboyStudent() s3=OldboyStudent() #如此,s1、s2、s3都一樣了,而這三者除了相似的屬性之外還各種不同的屬性,這就用到了__init__ #注意:該方法是在對象產生之后才會執行,只用來為對象進行初始化操作,可以有任意代碼,但一定不能有返回值 class OldboyStudent: ...... def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex ...... s1=OldboyStudent('李坦克','男',18) #先調用類產生空對象s1,然后調用OldboyStudent.__init__(s1,'李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴彈','男',78) #程序中對象的用法 #執行__init__,s1.name='牛榴彈',很明顯也會產生對象的名稱空間 s2.__dict__ {'name': '王大炮', 'age': '女', 'sex': 38} s2.name #s2.__dict__['name'] s2.name='王三炮' #s2.__dict__['name']='王三炮' s2.course='python' #s2.__dict__['course']='python' del s2.course #s2.__dict__.pop('course')
!!!細說__init__方法!!!
#方式一、為對象初始化自己獨有的特征 class People: country='China' x=1 def run(self): print('----->', self) # 實例化出三個空對象 obj1=People() obj2=People() obj3=People() # 為對象定制自己獨有的特征 obj1.name='egon' obj1.age=18 obj1.sex='male' obj2.name='lxx' obj2.age=38 obj2.sex='female' obj3.name='alex' obj3.age=38 obj3.sex='female' # print(obj1.__dict__) # print(obj2.__dict__) # print(obj3.__dict__) # print(People.__dict__) #方式二、為對象初始化自己獨有的特征 class People: country='China' x=1 def run(self): print('----->', self) # 實例化出三個空對象 obj1=People() obj2=People() obj3=People() # 為對象定制自己獨有的特征 def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z chu_shi_hua(obj1,'egon',18,'male') chu_shi_hua(obj2,'lxx',38,'female') chu_shi_hua(obj3,'alex',38,'female') #方式三、為對象初始化自己獨有的特征 class People: country='China' x=1 def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z def run(self): print('----->', self) obj1=People() # print(People.chu_shi_hua) People.chu_shi_hua(obj1,'egon',18,'male') obj2=People() People.chu_shi_hua(obj2,'lxx',38,'female') obj3=People() People.chu_shi_hua(obj3,'alex',38,'female') # 方式四、為對象初始化自己獨有的特征 class People: country='China' x=1 def __init__(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z def run(self): print('----->', self) obj1=People('egon',18,'male') #People.__init__(obj1,'egon',18,'male') obj2=People('lxx',38,'female') #People.__init__(obj2,'lxx',38,'female') obj3=People('alex',38,'female') #People.__init__(obj3,'alex',38,'female') # __init__方法 # 強調: # 1、該方法內可以有任意的python代碼 # 2、一定不能有返回值 class People: country='China' x=1 def __init__(obj, name, age, sex): #obj=obj1,x='egon',y=18,z='male' # if type(name) is not str: # raise TypeError('名字必須是字符串類型') obj.name = name obj.age = age obj.sex = sex def run(self): print('----->', self) # obj1=People('egon',18,'male') obj1=People(3537,18,'male') # print(obj1.run) # obj1.run() #People.run(obj1) # print(People.run)
PS:
1. 站的角度不同,定義出的類是截然不同的,詳見面向對象實戰之需求分析
2. 現實中的類並不完全等於程序中的類,比如現實中的公司類,在程序中有時需要拆分成部門類,業務類......
3. 有時為了編程需求,程序中也可能會定義現實中不存在的類,比如策略類,現實中並不存在,但是在程序中卻是一個很常見的類
#python為類內置的特殊屬性
類名.__name__# 類的名字(字符串)
類名.__doc__# 類的文檔字符串
類名.__base__# 類的第一個父類(在講繼承時會講)
類名.__bases__# 類所有父類構成的元組(在講繼承時會講)
類名.__dict__# 類的字典屬性
類名.__module__# 類定義所在的模塊
類名.__class__# 實例對應的類(僅新式類中)
!!!補充說明:從代碼級別看面向對象 !!!
#1、在沒有學習類這個概念時,數據與功能是分離的 def exc1(host,port,db,charset): conn=connect(host,port,db,charset) conn.execute(sql) return xxx def exc2(host,port,db,charset,proc_name) conn=connect(host,port,db,charset) conn.call_proc(sql) return xxx #每次調用都需要重復傳入一堆參數 exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;') exc2('127.0.0.1',3306,'db1','utf8','存儲過程的名字') #2、我們能想到的解決方法是,把這些變量都定義成全局變量 HOST=‘127.0.0.1’ PORT=3306 DB=‘db1’ CHARSET=‘utf8’ def exc1(host,port,db,charset): conn=connect(host,port,db,charset) conn.execute(sql) return xxx def exc2(host,port,db,charset,proc_name) conn=connect(host,port,db,charset) conn.call_proc(sql) return xxx exc1(HOST,PORT,DB,CHARSET,'select * from tb1;') exc2(HOST,PORT,DB,CHARSET,'存儲過程的名字') #3、但是2的解決方法也是有問題的,按照2的思路,我們將會定義一大堆全局變量,這些全局變量並沒有做任何區分,即能夠被所有功能使用,然而事實上只有HOST,PORT,DB,CHARSET是給exc1和exc2這兩個功能用的。言外之意:我們必須找出一種能夠將數據與操作數據的方法組合到一起的解決方法,這就是我們說的類了 class MySQLHandler: def __init__(self,host,port,db,charset='utf8'): self.host=host self.port=port self.db=db self.charset=charset def exc1(self,sql): conn=connect(self.host,self.port,self.db,self.charset) res=conn.execute(sql) return res def exc2(self,sql): conn=connect(self.host,self.port,self.db,self.charset) res=conn.call_proc(sql) return res obj=MySQLHandler('127.0.0.1',3306,'db1') obj.exc1('select * from tb1;') obj.exc2('存儲過程的名字') #改進 class MySQLHandler: def __init__(self,host,port,db,charset='utf8'): self.host=host self.port=port self.db=db self.charset=charset self.conn=connect(self.host,self.port,self.db,self.charset) def exc1(self,sql): return self.conn.execute(sql) def exc2(self,sql): return self.conn.call_proc(sql) obj=MySQLHandler('127.0.0.1',3306,'db1') obj.exc1('select * from tb1;') obj.exc2('存儲過程的名字')
四、屬性查找
類有兩種屬性:數據屬性和函數屬性
1. 類的數據屬性是所有對象共享的
2. 類的函數屬性是綁定給對象用的
#類的數據屬性是所有對象共享的,id都一樣 print(id(OldboyStudent.school)) print(id(s1.school)) print(id(s2.school)) print(id(s3.school)) ''' 4377347328 4377347328 4377347328 4377347328 ''' #類的函數屬性是綁定給對象使用的,obj.method稱為綁定方法,內存地址都不一樣 #ps:id是python的實現機制,並不能真實反映內存地址,如果有內存地址,還是以內存地址為准 print(OldboyStudent.learn) print(s1.learn) print(s2.learn) print(s3.learn) ''' <function OldboyStudent.learn at 0x1021329d8> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x1021466d8>> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146710>> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146748>> '''

在obj.name會先從obj自己的名稱空間里找name,找不到則去類中找,類也找不到就找父類...最后都找不到就拋出異常
五、綁定到對象的方法的特殊之處
#改寫 class OldboyStudent: school='oldboy' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def learn(self): print('%s is learning' %self.name) #新增self.name def eat(self): print('%s is eating' %self.name) def sleep(self): print('%s is sleeping' %self.name) s1=OldboyStudent('李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴彈','男',78)
類中定義的函數(沒有被任何裝飾器裝飾的)是類的函數屬性,類可以使用,但必須遵循函數的參數規則,有幾個參數需要傳幾個參數
OldboyStudent.learn(s1) #李坦克 is learning OldboyStudent.learn(s2) #王大炮 is learning OldboyStudent.learn(s3) #牛榴彈 is learning
類中定義的函數(沒有被任何裝飾器裝飾的),其實主要是給對象使用的,而且是綁定到對象指向的都是相同的功能,但是綁定到不同的對象就是不同的綁定方法
強調:綁定到對象的方法的特殊之處在於,綁定給誰就由誰來調用,誰來調用,就會將'誰'本身當作第一個參數傳給方法,即自動傳值(方法__init__也是一樣的道理)
s1.learn() #等同於OldboyStudent.learn(s1)
s2.learn() #等同於OldboyStudent.learn(s2)
s3.learn() #等同於OldboyStudent.learn(s3)
注意:綁定到對象的方法的這種自動傳值的特征,決定了在類中定義的函數都要默認寫一個參數self,self可以是任意名字,但是約定俗稱地寫slef。
類即類型
提示:Python的class術語與c++有一定的區別,與Modula-3更像。
Python中一切皆對象,且Python3中類與類型是一個概念,類型就是類
#類型dict就是類dict >>> list <class 'list'> #實例化的到3個對象l1,l2,l3 >>> l1=list() >>> l2=list() >>> l3=list() #三個對象都有綁定方法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(3),就是在往l1添加3,絕對不會將3添加到l2或l3 >>> l1.append(3) >>> l1 [3] >>> l2 [] >>> l3 [] #調用類list.append(l3,111)等同於l3.append(111) >>> list.append(l3,111) #l3.append(111) >>> l3 [111]
六、對象之間的交互
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 #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值。
我們可以仿照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 #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值。
示例出倆英雄
>>> g1=Garen('草叢倫') >>> r1=Riven('銳雯雯')
交互:銳雯雯攻擊草叢倫,反之一樣
>>> g1.life_value 455 >>> r1.attack(g1) >>> g1.life_value 401
補充:
garen_hero.Q()稱為向garen_hero這個對象發送了一條消息,讓他去執行Q這個功能,類似的有:
garen_hero.W()
garen_hero.E()
garen_hero.R()
