day22 面向對象初級
面向過程 VS 面向對象
面向過程的程序設計:核心是過程二字,過程指的是解決問題的步驟,即先幹什麽再幹什麽......面向過程的設計就好比精心設計好一條流水線,是一種機械式的思維方式。
優點是:復雜度的問題簡單化、流程化
缺點是:一套流水線或者流程就是用來解決一個問題,生產汽水的流水線無法生產汽車,即便是能,也得是大改,改一個組件,牽一發而動全身。
應用場景:一旦完成基本很少改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向對象的程序設計:核心是對象二字,(要理解對象為何物,必須把自己當成上帝,上帝眼裏世間存在的萬物皆為對象,不存在的也可以創造出來。面向對象的程序設計好比如來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題需要四個人:唐僧,沙和尚,豬八戒,孫悟空,每個人都有各自的特征和技能(這就是對象的概念,特征和技能分別對應對象的數據屬性和方法屬性),然而這並不好玩,於是如來又安排了一群妖魔鬼怪,為了防止師徒四人在取經路上被搞死,又安排了一群神仙保駕護航,這些都是對象。然後取經開始,師徒四人與妖魔鬼怪神仙交互著直到最後取得真經。如來根本不會管師徒四人按照什麽流程去取),對象是特征與技能的結合體,基於面向對象設計程序就好比在創造一個世界,你就是這個世界的上帝,存在的皆為對象,不存在的也可以創造出來,比較面向過程,面向對象更加註重對現實世界的模擬,是一種“上帝式”的思維方式。
優點是:解決了程序的擴展性。對某一個對象單獨修改,會立刻反映到整個體系中,如對遊戲中一個人物參數的特征和技能修改都很容易。
缺點:
1. 編程的復雜度遠高於面向過程,不了解面向對象而立即上手基於它設計程序,極容易出現過度設計的問題。一些擴展性要求低的場景使用面向對象會徒增編程難度,比如管理linux系統的shell腳本就不適合用面向對象去設計,面向過程反而更加適合。
2. 無法向面向過程的程序設計流水線式的可以很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題,即便是上帝也無法準確地預測最終結果。於是我們經常看到對戰類遊戲,新增一個遊戲人物,在對戰的過程中極容易出現陰霸的技能,一刀砍死3個人,這種情況是無法準確預知的,只有對象之間交互才能準確地知道最終的結果。
應用場景:需求經常變化的軟件,一般需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方
面向對象的程序設計並不是全部。對於一個軟件質量來說,面向對象的程序設計只是用來解決擴展性。
類即類別、種類,是面向對象設計最重要的概念,對象是特征與技能的結合體,而類則是一系列對象相似的特征與技能的結合體
那麽問題來了,先有的一個個具體存在的對象(比如一個具體存在的人),還是先有的人類這個概念,這個問題需要分兩種情況去看
在現實世界中:先有對象,再有類
世界上肯定是先出現各種各樣的實際存在的物體,然後隨著人類文明的發展,人類站在不同的角度總結出了不同的種類,如人類、動物類、植物類等概念
也就說,對象是具體的存在,而類僅僅只是一個概念,並不真實存在
在程序中:務必保證先定義類,後產生對象
這與函數的使用是類似的,先定義函數,後調用函數,類也是一樣的,在程序中需要先定義類,後調用類
不一樣的是,調用函數會執行函數體代碼返回的是函數體執行的結果,而調用類會產生對象,返回的是對象
按照上述步驟,我們來定義一個類(我們站在老男孩學校的角度去看,在座的各位都是垃圾,sorry,都是學生)
聲明類
‘‘‘ class 類名: ‘類的文檔字符串‘ 類體 ‘‘‘ #我們創建一個類 class Data: pass 聲明類
class Person: #定義一個人類 role = ‘person‘ #人的角色屬性都是人 def walk(self): #人都可以走路,也就是有一個走路方法,也叫動態屬性 print("person is walking...")
#在現實世界中,站在老男孩學校的角度:先有對象,再有類 對象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‘) 在程序中:先定義類,後產生對象
一:我們定義的類的屬性到底存到哪裏了?有兩種方式查看
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key為屬性名,value為屬性值
二:特殊的類屬性
類名.__name__# 類的名字(字符串)
類名.__doc__# 類的文檔字符串
類名.__base__# 類的第一個父類(在講繼承時會講)
類名.__bases__# 類所有父類構成的元組(在講繼承時會講)
類名.__dict__# 類的字典屬性
類名.__module__# 類定義所在的模塊
類名.__class__# 實例對應的類(僅新式類中)
類屬性的補充
類有兩種作用:屬性引用和實例化
屬性引用(類名.屬性)
class Person: #定義一個人類 role = ‘person‘ #人的角色屬性都是人 def walk(self): #人都可以走路,也就是有一個走路方法 print("person is walking...") print(Person.role) #查看人的role屬性 print(Person.walk) #引用人的走路方法,註意,這裏不是在調用
實例化:類名加括號就是實例化,會自動觸發__init__函數的運行,可以用它來為每個實例定制自己的特征
class Person: #定義一個人類 role = ‘person‘ #人的角色屬性都是人 def __init__(self,name): self.name = name # 每一個角色都有自己的昵稱; def walk(self): #人都可以走路,也就是有一個走路方法 print("person is walking...") print(Person.role) #查看人的role屬性 print(Person.walk) #引用人的走路方法,註意,這裏不是在調用
#類型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 []
實例化的過程就是類——>對象的過程
原本我們只有一個Person類,在這個過程中,產生了一個egg對象,有自己具體的名字、攻擊力和生命值。
語法:對象名 = 類名(參數)
egg = Person(‘egon‘) #類名()就等於在執行Person.__init__() #執行完__init__()就會返回一個對象。這個對象類似一個字典,存著屬於這個人本身的一些屬性和方法。 #你可以偷偷的理解:egg = {‘name‘:‘egon‘,‘walk‘:walk}
查看屬性&調用方法
rint(egg.name) #查看屬性直接 對象名.屬性名 print(egg.walk()) #調用方法,對象名.方法名()
關於self
self:在實例化時自動將對象/實例本身傳給__init__的第一個參數,你也可以給他起個別的名字,但是正常人都不會這麽做。
因為你瞎改別人就不認識
對象的相關知識
class Person: # 定義一個人類 role = ‘person‘ # 人的角色屬性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一個角色都有自己的昵稱; self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力; self.life_value = life_value # 每一個角色都有自己的生命值; def attack(self,dog): # 人可以攻擊狗,這裏的狗也是一個對象。 # 人攻擊狗,那麽狗的生命值就會根據人的攻擊力而下降 dog.life_value -= self.aggressivit
對象是關於類而實際存在的一個例子,即實例
對象/實例只有一種作用:屬性引用
egg = Person(‘egon‘,10,1000) print(egg.name) print(egg.aggressivity) print(egg.life_value)
當然了,你也可以引用一個方法,因為方法也是一個屬性,只不過是一個類似函數的屬性,我們也管它叫動態屬性。
引用動態屬性並不是執行這個方法,要想調用方法和調用函數是一樣的,都需要在後面加上括號
print(egg.attack)
我知道在類裏說,你可能還有好多地方不能理解。那我們就用函數來解釋一下這個類呀,對象呀到底是個啥,你偷偷的用這個理解就好了,不要告訴別人
def Person(*args,**kwargs): self = {} def attack(self,dog): dog[‘life_value‘] -= self[‘aggressivity‘] def __init__(name,aggressivity,life_value): self[‘name‘] = name self[‘aggressivity‘] = aggressivity self[‘life_value‘] = life_value self[‘attack‘] = attack __init__(*args,**kwargs) return self egg = Person(‘egon‘,78,10) print(egg[‘name‘]) 幫你了解面向對象
面向對象小結——定義及調用的固定模式
class 類名: def __init__(self,參數1,參數2): self.對象的屬性1 = 參數1 self.對象的屬性2 = 參數2 def 方法名(self):pass def 方法名2(self):pass 對象名 = 類名(1,2) #對象就是實例,代表一個具體的東西 #類名() : 類名+括號就是實例化一個類,相當於調用了__init__方法 #括號裏傳參數,參數不需要傳self,其他與init中的形參一一對應 #結果返回一個對象 對象名.對象的屬性1 #查看對象的屬性,直接用 對象名.屬性名 即可 對象名.方法名() #調用類中的方法,直接用 對象名.方法名() 即可 小結
對象之間的交互
現在我們已經有一個人類了,通過給人類一些具體的屬性我們就可以拿到一個實實在在的人。
現在我們要再創建一個狗類,狗就不能打人了,只能咬人,所以我們給狗一個bite方法。
有了狗類,我們還要實例化一只實實在在的狗出來。
然後人和狗就可以打架了。現在我們就來讓他們打一架吧!
創建一個狗類
class Dog: # 定義一個狗類 role = ‘dog‘ # 狗的角色屬性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵稱; self.breed = breed # 每一只狗都有自己的品種; self.aggressivity = aggressivity # 每一只狗都有自己的攻擊力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self,people): # 狗可以咬人,這裏的狗也是一個對象。 # 狗咬人,那麽人的生命值就會根據狗的攻擊力而下降 dog.life_value -= self.aggressivit
實例化一只實實在在的二哈
ha2 = Dog(‘二楞子‘,‘哈士奇‘,10,1000) #創造了一只實實在在的狗ha2
交互 egon打ha2一下
print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10點血
完整的代碼
class Person: # 定義一個人類 role = ‘person‘ # 人的角色屬性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一個角色都有自己的昵稱; self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力; self.life_value = life_value # 每一個角色都有自己的生命值; def attack(self,dog): # 人可以攻擊狗,這裏的狗也是一個對象。 # 人攻擊狗,那麽狗的生命值就會根據人的攻擊力而下降 dog.life_value -= self.aggressivity class Dog: # 定義一個狗類 role = ‘dog‘ # 狗的角色屬性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵稱; self.breed = breed # 每一只狗都有自己的品種; self.aggressivity = aggressivity # 每一只狗都有自己的攻擊力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self,people): # 狗可以咬人,這裏的狗也是一個對象。 # 狗咬人,那麽人的生命值就會根據狗的攻擊力而下降 people.life_value -= self.aggressivity egg = Person(‘egon‘,10,1000) #創造了一個實實在在的人egg ha2 = Dog(‘二楞子‘,‘哈士奇‘,10,1000) #創造了一只實實在在的狗ha2 print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10點血 egon大戰哈士奇
類命名空間與對象、實例的命名空間
創建一個類就會創建一個類的名稱空間,用來存儲類中定義的所有名字,這些名字稱為類的屬性
而類有兩種屬性:靜態屬性和動態屬性
- 靜態屬性就是直接在類中定義的變量
- 動態屬性就是定義在類中的方法
其中類的數據屬性是共享給所有對象的
>>>id(egg.role) 4341594072 >>>id(Person.role) 4341594072
而類的動態屬性是綁定到所有對象的
>>>egg.attack <bound method Person.attack of <__main__.Person object at 0x101285860>> >>>Person.attack <function Person.attack at 0x10127abf8>
創建一個對象/實例就會創建一個對象/實例的名稱空間,存放對象/實例的名字,稱為對象/實例的屬性
在obj.name會先從obj自己的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常
面向對象的組合用法
軟件重用的重要方式除了繼承之外還有另外一種方式,即:組合
組合指的是,在一個類中以另外一個類的對象作為數據屬性,稱為類的組合
class Weapon: def prick(self, obj): # 這是該裝備的主動技能,紮死對方 obj.life_value -= 500 # 假設攻擊力是500 class Person: # 定義一個人類 role = ‘person‘ # 人的角色屬性都是人 def __init__(self, name): self.name = name # 每一個角色都有自己的昵稱; self.weapon = Weapon() # 給角色綁定一個武器; egg = Person(‘egon‘) egg.weapon.prick() #egg組合了一個武器的對象,可以直接egg.weapon來使用組合類中的所有方法
用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的關系,比如教授有生日,教授教python課程
class BirthDate: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class Couse: def __init__(self,name,price,period): self.name=name self.price=price self.period=period class Teacher: def __init__(self,name,gender): self.name=name self.gender=gender def teach(self): print(‘teaching‘) class Professor(Teacher): def __init__(self,name,gender,birth,course): Teacher.__init__(self,name,gender) self.birth=birth self.course=course p1=Professor(‘egon‘,‘male‘, BirthDate(‘1995‘,‘1‘,‘27‘), Couse(‘python‘,‘28000‘,‘4 months‘)) print(p1.birth.year,p1.birth.month,p1.birth.day) print(p1.course.name,p1.course.price,p1.course.period) ‘‘‘ 運行結果: 27 python 28000 4 months ‘‘‘
當類之間有顯著不同,並且較小的類是較大的類所需要的組件時,用組合比較好
初識面向對象小結
定義一個人類
class Person: # 定義一個人類 role = ‘person‘ # 人的角色屬性都是人 def __init__(self, name, aggressivity, life_value, money): self.name = name # 每一個角色都有自己的昵稱; self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力; self.life_value = life_value # 每一個角色都有自己的生命值; self.money = money def attack(self,dog): # 人可以攻擊狗,這裏的狗也是一個對象。 # 人攻擊狗,那麽狗的生命值就會根據人的攻擊力而下降 dog.life_value -= self.aggressivity
定義一個狗類
class Dog: # 定義一個狗類 role = ‘dog‘ # 狗的角色屬性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵稱; self.breed = breed # 每一只狗都有自己的品種; self.aggressivity = aggressivity # 每一只狗都有自己的攻擊力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self,people): # 狗可以咬人,這裏的狗也是一個對象。 # 狗咬人,那麽人的生命值就會根據狗的攻擊力而下降 people.life_value -= self.aggressivity
接下來,又創建一個新的兵器類。
class Weapon: def __init__(self,name, price, aggrev, life_value): self.name = name self.price = price self.aggrev = aggrev self.life_value = life_value def update(self, obj): #obj就是要帶這個裝備的人 obj.money -= self.price # 用這個武器的人花錢買所以對應的錢要減少 obj.aggressivity += self.aggrev # 帶上這個裝備可以讓人增加攻擊 obj.life_value += self.life_value # 帶上這個裝備可以讓人增加生命值 def prick(self, obj): # 這是該裝備的主動技能,紮死對方 obj.life_value -= 500 # 假設攻擊力是500
測試交互
lance = Weapon(‘長矛‘,200,6,100) egg = Person(‘egon‘,10,1000,600) #創造了一個實實在在的人egg ha2 = Dog(‘二楞子‘,‘哈士奇‘,10,1000) #創造了一只實實在在的狗ha2 #egg獨自力戰"二楞子"深感吃力,決定窮畢生積蓄買一把武器 if egg.money > lance.price: #如果egg的錢比裝備的價格多,可以買一把長矛 lance.update(egg) #egg花錢買了一個長矛防身,且自身屬性得到了提高 egg.weapon = lance #egg裝備上了長矛 print(egg.money,egg.life_value,egg.aggressivity) print(ha2.life_value) egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) egg.weapon.prick(ha2) #發動武器技能 print(ha2.life_value) #ha2不敵狡猾的人類用武器取勝,血槽空了一半
day22 面向對象初級