python开发学习笔记之六(面向对象)
面向對象引入:
現在有一個這樣的需求:做汽水。
在之前的學習中,我們怎樣處理這種類似的問題呢?思考一下,哦,不就是分步驟做嘛,把復雜的問題簡單化,分成一個一個的步驟,就像機器人,流水線一樣,先干什么后干什么,然后干什么,最后再干什么。于是這個問題就可以分解成這幾個步驟:
制作汽水瓶———制作汽水————把汽水灌入汽水瓶————封口————貼標簽————打包裝箱
這就是一套完整的汽水制作生產線,這就是典型的面向過程的程序設計思路。
面向過程的優缺點:
優點:復雜問題流程化,進而簡單化。將問題分解成步驟,逐個步驟進行處理
缺點:針對性太強,一套方法只適用于一種需求,代碼的可重用性太低,只適用于一些一旦寫好不進行修改的場景。
而面向對象是站在上帝角度來看待問題,上面做汽水的流水線,就可以看做是一個對象。這個對象可以做汽水瓶,可以做汽水,可以灌裝,等等一系列的操作。
對象的概念就是:特征與技能的結合體(人就是個對象,人有名字年齡性別,這是特征;人會吃飯睡覺學習,這是技能。)
面向對象的優缺點:
優點:提高了代碼的擴展性,對某個對象進行修改,可以影響到整個代碼體系
缺點:編程的復雜度提高,無法準確地預測最終結果。 適用于需求不斷變化的場景
?
類與對象:
?
類即類別、種類,是面向對象設計最重要的概念,對象是特征與技能的結合體,而類則是一系列對象相似的特征與技能的結合體
現實世界中,先有對象,后有類:
#在現實世界中,站在老男孩學校的角度:先有對象,再有類 對象1:李坦克特征:學校=oldboy姓名=李坦克性別=男年齡=18技能:學習吃飯睡覺對象2:王大炮特征:學校=oldboy姓名=王大炮性別=女年齡=38技能:學習吃飯睡覺對象3:牛榴彈特征:學校=oldboy姓名=牛榴彈性別=男年齡=78技能:學習吃飯睡覺現實中的oldboy學生類相似的特征:學校=oldboy相似的技能:學習吃飯睡覺 View Code在程序中,先定義類,后實例化出對象:
class Student:school = 'oldboy' # 相同的特征def __init__(self, name, age, sex) # 各自的特征self.name = nameself.age = ageself.sex = sexdef eat(self): # 相似的技能print('eating...')def learn(self):print('learning...')def sleep(self)print('sleeping...') View Code? 上面的代碼就是定義了一個學生類
獲得對象就是通過類實例化出來
stu1 = Student('zhangsan','18,'男')類的使用:
定義Student類:
class Student:school='oldboy'def learn(self):print('is learning')def eat(self):print('is eating')def sleep(self):print('is sleeping')# 查看類的名稱空間
Student.__dict__
查看類中的屬性:
Student.__dict__['school']? ?就可以獲得類的數據屬性,python 專用的屬性訪問方法是 Student.school
設置類中的屬性:
增加屬性:Student.county = 'China' 修改屬性:Student.school = 'OLDBOY'
刪除類中的屬性:
del Student.school
?
對象的使用:
先生成三個對象:
stu1 = Student()
stu2 = Student()
stu3 = Student()
這三個學生對象都有相同的學校和相同的技能,但是這三名學生也有各自獨有的屬性:姓名,年齡,性別,那么怎么個性化定制每個學生的信息呢?
這就用到了類內部的__init__方法,它在實例化出對象時就會執行,給對象創建出各自不同的屬性
# 定義類 class Student:school = 'oldboy'def __init__(self, name, age, sex): # 為對象定制各自的數據屬性self.Name = nameself.Age = ageself.Sex = sexdef eat(self):print('is eating...')def learn(self):print('is learning...')def sleep(self):print('is sleeping...')# 實例化對象
stu1 = Student('zhangsan',18,'male')
它實例化的具體步驟是:先產生一個空對象,再通過__init__方法給空對象添加屬性
查看對象的屬性:
stu1.name
stu1.learn()
設置對象的屬性:
增加屬性:stu1.class_name = 'python開發' 修改屬性:stu1.name = 'lisi'
刪除對象的屬性:
del stu1.name
?
注意:在類中的數據屬性是所有對象所直接共有的,是同一個id;而類中的函數屬性是分別綁定到每個不同的對象,是不同的id.
當通過類調用類內部的方法時需要把對象當做參數傳入函數:
Student.learn(stu1)
當實例化出一個對象時,對象會綁定類內的函數為對象自己的綁定方法。實際上,類內的函數屬性就是給對象使用的
而通過對象來調用自己的綁定方法時,會自動把對象傳入函數當做參數(誰來調用,就把誰當做參數傳入函數):
stu1.learn()
查找屬性會先從對象自己內部查找屬性,如果對象內部沒有這個屬性,就會從這個對象的類里去查找屬性,類似與函數的局部變量與全局變量
?
補充:
1.站的角度不同,定義的類也不同
2.編程中可能定義現實生活中不存在的類,比如關系類,策略類等等
ps: python中一切皆對象,在python3中統一了類與類型的概念
練習一:
編寫學生類,實例化一些學生對象,并加上計數器(屬性),統計實例化的對象個數:
class Student: # 生成學生類school = 'oldboy'count = 0 # 生成計數器def __init__(self, name, age, sex):self.Name = nameself.Age = ageself.Sex = sexStudent.count += 1 # 每實例化一個對象把類的計數器加一def eat(self):print('is eating...')def learn(self):print('is learning...')def sleep(self):print('is sleeping...')stu1 = Student('zhangsan', 18, 'male') stu2 = Student('lisi', 28, 'female') stu3 = Student('wangwu', 38, 'male')print(Student.count)?
練習二:
模仿LOL,生成游戲角色對象,角色可以互相攻擊
?
class Garen: # 定義蓋倫類def __init__(self, nickname, hp, ap):self.nickname = nicknameself.hp = hpself.ap = apdef attack(self, enemy):enemy.hp -= self.apclass Reven: # 定義瑞文類def __init__(self, nickname, hp, ap):self.nickname = nicknameself.hp = hpself.ap = apdef attack(self, enemy):enemy.hp -= self.apg = Garen('草叢倫', 500, 30)r = Reven('瑞雯雯', 300, 50)print('r', r.hp) g.attack(r) print('r', r.hp)看一下上面的代碼,你會發現兩個類內部的代碼是一模一樣的,那么如果再需要定義其他角色就要重復寫同樣的代碼,怎么解決代碼重復性的問題呢?
這就需要了解一個概念:
繼承:
繼承指的是類與類之間的關系,是一種什么“是”什么的關系,繼承的功能之一就是用來解決代碼重用問題
繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可以成為基類或超類,新建的類稱為派生類或子類
class Hero:def __init__(self, nickname, hp, ap):self.nickname = nicknameself.hp = hpself.ap = apdef attack(self, enemy):enemy.hp -= self.apclass Garen(Hero):passclass Reven(Hero):passg = Garen('草叢倫', 500, 30)r = Reven('瑞雯雯', 300, 50)print('r', r.hp) g.attack(r) print('r', r.hp)上面的代可以寫成這種形式,兩個角色類可以繼承英雄類,從而提高了代碼的復用性。
單繼承與多繼承
在python中可以繼承多個父類:
class ParentClass1: #定義父類passclass ParentClass2: #定義父類passclass SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClasspassclass SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類pass查看繼承關系:
SubClass1.__bases__有了繼承關系,通過對象去查找屬性是就是這樣:
對象本身——對象的類————類的父類——...
一層一層的查找,找到就不往上找了
?
派生:
子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調用新增的屬性時,就以自己的屬性為準。
class Riven(Hero):camp='Noxus'def attack(self,enemy): #在自己這里定義新的attack,不再使用父類的attack,且不會影響父類print('from riven')def fly(self): #在自己這里定義新的print('%s is flying' %self.nickname)繼承的實現原理
當定義一個類時,python會為這個類生成一個方法解釋順序mro列表,為了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。而這個MRO列表的構造是通過一個C3線性化算法來實現的。我們不去深究這個算法的數學原理,它實際上就是合并所有父類的MRO列表并遵循如下三條準則:
在Java和C#中子類只能繼承一個父類,而Python中子類可以同時繼承多個父類,如果繼承了多個父類,那么屬性的查找方式有兩種,分別是:深度優先和廣度優先
在python3中所有的類都是新式類,新式類就是在不指定繼承關系的情況下,默認繼承object類。在一系列的繼承之后,最頂端的父類必然是object類
?
接下來思考一個問題,如果想在子類中想復用一下父類的方法,需要怎么辦呢?
比如:
class Hero:def __init__(self, nickname, hp, ap):self.nickname = nicknameself.hp = hpself.ap = apdef attack(self, enemy):enemy.hp -= self.apclass Garen(Hero):def attack(self, enemy):#<---在這里重用父類的攻擊方法print('Garen attack!')pass
class Reven(Hero):passg = Garen('草叢倫', 500, 30) r = Reven('瑞雯雯', 300, 50)
那么你可能會想到下面這種方法:
class Hero:def __init__(self, nickname, hp, ap):self.nickname = nicknameself.hp = hpself.ap = apdef attack(self, enemy):enemy.hp -= self.apclass Garen(Hero):def attack(self, enemy):Hero.attack(self, enemy) #<---在這里重用父類的攻擊方法print('Garen attack!')passclass Reven(Hero):passg = Garen('草叢倫', 500, 30)r = Reven('瑞雯雯', 300, 50)這種就是指名道姓的調用方法,不依賴于繼承關系
第二種就是依賴于繼承關系的?super()?方法:
class Hero:def __init__(self, nickname, hp, ap):self.nickname = nicknameself.hp = hpself.ap = apdef attack(self, enemy):enemy.hp -= self.apclass Garen(Hero):def attack(self, enemy):super().attack(enemy) #<---在這里重用父類的攻擊方法print('Garen attack!')passclass Reven(Hero):passg = Garen('草叢倫', 500, 30)r = Reven('瑞雯雯', 300, 50)?組合:
組合指的就是是在一個類里以另一個類中的對象為數據屬性,也就是什么“有”什么的關系:
舉個上文中的例子:
>>> class Equip: #武器裝備類 ... def fire(self): ... print('release Fire skill') ... >>> class Riven: #英雄Riven的類,一個英雄需要有裝備,因而需要組合Equip類 ... camp='Noxus' ... def __init__(self,nickname): ... self.nickname=nickname ... self.equip=Equip() #用Equip類產生一個裝備,賦值給實例的equip屬性 ... >>> r1=Riven('銳雯雯') >>> r1.equip.fire() #可以使用組合的類產生的對象所持有的方法 release Fire skill組合與繼承都是有效地利用已有類的資源的重要方式。但是二者的概念和使用場景皆不同,
1.繼承的方式
通過繼承建立了派生類與基類之間的關系,它是一種'是'的所屬關系,比如白馬是馬,人是動物。
當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如老師是人,學生是人
2.組合的方式
用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的擁有關系,比如教授有生日,教授教python和linux課程,教授有學生s1、s2、s3...
?
抽象類與歸一化:
抽取子類比較相像的部分,統一規范類中的屬性調用方法,這用到了一個叫 abc 的模塊
import abcclass Animal(metaclass=abc.ABCMeta):@abc.abstractmethoddef run(self):pass@abc.abstractmethoddef eat(self):passclass People(Animal):passclass Pig(Animal):def run(self):print('pig is runing')def eat(self):print('pig is eating')pig1 = Pig() peo1 = People()當People類沒有用抽象類規范的方法時就會報錯。通過抽象類實現了歸一化的作用。
?
多態與多態性:
?
?
?
?
?
轉載于:https://www.cnblogs.com/wilbur0402/p/9518340.html
總結
以上是生活随笔為你收集整理的python开发学习笔记之六(面向对象)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习笔记(43):Python实战编程-
- 下一篇: 学习笔记(44):Python实战编程-