參考:https://www.cnblogs.com/Eva-J/articles/7293890.htm#_label14html
講解及其詳細。你們能夠直接去看原創。python
Python中一切皆爲對象。git
面向對象編程簡單來講就是基於對 類 和 對象 的使用,全部的代碼都是經過類和對象來實現的編程就是面向對象編程!程序員
類:一個模板,模板裏能夠包含多個函數,函數裏實現一些功能。具備相同特徵的一類事物。編程
對象(實例):根據模板建立的實例,經過實例對象能夠執行類中的函數。具體的某一個事物。python3.x
屬性:事務的特徵。好比好比年齡、身高、性別、姓名等都叫作屬性,一個類中,能夠有多個屬性。安全
方法:人類不止有身高、年齡、性別這些屬性,還能作好多事情,好比說話、走路、吃飯等,相比較於屬性是名詞,說話、走路是動詞,這些動詞用程序來描述就叫作方法。app
實例化:類——>對象的過程。編程語言
在python中,用變量表示特徵,用函數表示技能,於是具備相同特徵和技能的一類事物就是‘類’,對象是則是這一類事物中具體的一個。函數
核心是過程二字,過程指的是解決問題的步驟,比如如設計一條流水線,是一種機械式的思惟方式。
就是程序從上到下一步步執行,一步步從上到下,從頭至尾的解決問題 。
基本設計思路就是程序一開始是要着手解決一個大的問題,而後把一個大問題分解成不少個小問題或子過程,這些子過程再執行的過程再繼續分解直到小問題足夠簡單到能夠在一個小步驟範圍內解決。
優勢
將複雜的問題流程化,進而簡單化;
極大的下降了寫程序的複雜度,只須要順着要執行的步驟,堆疊代碼便可。
缺點
擴展性差;
代碼牽一髮而動全身。
應用場景
一旦完成基本不多改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等
核心是對象二字,特徵和技能的結合體。
優勢
可擴展性高;
對某一個對象單獨修改,會馬上反映到整個體系中,如對遊戲中一我的物參數的特徵和技能修改都很容易。
缺點
編程複雜度高;
可控性差,沒法向面向過程的程序設計流水線式的能夠很精準的預測問題的處理流程與結果,
面向對象的程序一旦開始就由對象之間的交互解決問題,即使是上帝也沒法預測最終結果。
因而咱們常常看到一個遊戲人某一參數的修改極有可能致使陰霸的技能出現,一刀砍死3我的,這個遊戲就失去平衡。
應用場景
需求常常變化的軟件,通常需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方。
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 #查看對象的屬性,直接用 對象名.屬性名 便可 對象名.方法名() #調用類中的方法,直接用 對象名.方法名() 便可
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 # 每一隻狗都有本身的生命值;
ha2 = Dog('二愣子','哈士奇',10,1000) #創造了一隻實實在在的狗ha2 #對象就是實例,表明一個具體的東西 #類名() : 類名+括號就是實例化一個類,至關於調用了__init__方法 #括號裏傳參數,參數不須要傳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.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點血
建立一個類就會建立一個類的名稱空間,用來存儲類中定義的全部名字,這些名字稱爲類的屬性。
而類有兩種屬性:靜態屬性和動態屬性
id(egg.role) == id(Person.role) #out:True
egg.attack == Person.attack #out:Fales
建立一個對象/實例就會建立一個對象/實例的名稱空間,存放對象/實例的名字,稱爲對象/實例的屬性
在obj.name會先從obj本身的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常。
軟件重用的重要方式除了繼承以外還有另一種方式,即:組合
組合指的是,在一個類中以另一個類的對象做爲數據屬性,稱爲類的組合。
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,birth,course): self.name=name self.gender=gender self.birth=birth self.course=course def teach(self): print('teaching') p1=Teacher('egon','male', BirthDate('1995','1','27'), Couse('python','28000','4 months') ) print(p1.birth.year,p1.birth.month,p1.birth.day) #out: 1995 1 27 print(p1.course.name,p1.course.price,p1.course.period) #out:python 28000 4 months
當類之間有顯著不一樣,而且較小的類是較大的類所須要的組件時,用組合比較好。
繼承是一種建立新類的方式,在python中,新建的類能夠繼承一個或多個父類,父類又可稱爲基類或超類,新建的類稱爲派生類或子類
在類名後面括號中寫上另外一個類,表示繼承了那個類。
抽象即抽取相似或者說比較像的部分。
抽象分紅兩個層次:
1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;
2.將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的做用是劃分類別(能夠隔離關注點,下降複雜度)
繼承:是基於抽象的結果,經過編程語言去實現它,確定是先經歷抽象這個過程,才能經過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程當中,一個動做或者說一種技巧,經過抽象能夠獲得類
class ParentClass1: #定義父類 pass class ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 pass #__base__只查看從左到右繼承的第一個子類,__bases__則是查看全部繼承的父類 print(SubClass1.__bases__) #out:(<class '__main__.ParentClass1'>,) print(SubClass2.__bases__) #out:(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) #若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見方法(如__str__)的實現。 print(ParentClass1.__bases__) #out:(<class 'object'>,)
在python中,類還能夠繼承多個類,在繼承多個類時,他對類中的函數查找有兩種方式
深度優先:類是經典類時,多繼承狀況下,會按照深度優先方式查找
廣度優先:類是新式類時,多繼承狀況下,會按照廣度優先方式查找
(在python3.x中)都默認爲廣度優先,但仍是能夠了解一下兩個的區別,新式類:當前類或者基類繼承了objiect類就叫新式類,否者就是經典類。
class A(object): def name(self): print("HELLO") class B(A): pass class C(A): def name(self): print("PYTHON") class D(B,C): pass a1=D() a1.name() #輸出:PYTHON #查找順序:# 首先去本身D類中查找,若是沒有,則繼續去B類中找,沒有則繼續去C類中找,沒有則繼續去A類中找,若是仍是未找到,則報錯 #廣度優先:D-B-C-A
#深度優先:D-B-A-C
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() #out:from D print(F.__mro__) #只有新式纔有這個屬性能夠查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類 繼承順序
在python3中,子類執行父類的方法也能夠直接用super方法。
super() 函數是用於調用父類(超類)的一個方法。
基本語法:super(type,[object-or-type])
type: 類
object-or-type:類,通常是 self
super().xxx 代替 super(Class, self).xxx
參考:http://www.javashuo.com/article/p-ztarpcjs-ea.html
class A(): def go(self): print ("go A go!") def stop(self): print ("stop A stop!") def pause(self): raise Exception("Not Implemented") class B(A): def go(self): super(B, self).go() print ("go B go!") class C(A): def go(self): super(C, self).go() print ("go C go!") def stop(self): super(C, self).stop() print ("stop C stop!") class D(B,C): def go(self): super(D, self).go() print ("go D go!") def stop(self): super(D, self).stop() print ("stop D stop!") def pause(self): print ("wait D wait!") class E(B,C): pass a = A() b = B() c = C() d = D() e = E() # 說明下列代碼的輸出結果 a.go()# go A go! b.go()# go A go!# go B go! c.go()# go A go!# go C go! d.go()# go A go!# go C go!# go B go!# go D go! e.go()# go A go!# go C go!# go B go! a.stop()# stop A stop! b.stop()# stop A stop! c.stop()# stop A stop!# stop C stop! d.stop()# stop A stop!# stop C stop!# stop D stop! e.stop()# stop A stop!# stop C stip! print(D.mro()) a.pause()# ... Exception: Not Implemented b.pause()# ... Exception: Not Implemented c.pause()# ... Exception: Not Implemented d.pause()# wait D wait! e.pause()# ...Exception: Not Implemented
子類能夠添加本身新的屬性或者在本身這裏從新定義這些屬性(不會影響到父類),須要注意的是,一旦從新定義了本身的屬性且與父類重名,那麼調用新增的屬性時,就以本身爲準了。
class Animal: ''' 人和狗都是動物,因此創造一個Animal基類 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有本身的暱稱; self.aggressivity = aggressivity # 人和狗都有本身的攻擊力; self.life_value = life_value # 人和狗都有本身的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗類,繼承Animal類 ''' def bite(self, people): ''' 派生:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity class Person(Animal): ''' 人類,繼承Animal ''' def attack(self, dog): ''' 派生:人有攻擊的技能 :param dog: ''' dog.life_value -= self.aggressivity egg = Person('egon',10,1000) ha2 = Dog('二愣子',50,1000) print(ha2.life_value) #out:1000 print(egg.attack(ha2)) #out:None print(ha2.life_value) #out:990
像ha2.life_value之類的屬性引用,會先從實例中找life_value而後去類中找,而後再去父類中找...直到最頂級的父類。
繼承有兩種用途:
接口繼承實質上是要求「作出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的全部對象」——這在程序設計上,叫作歸一化。
歸一化,讓使用者無需關心對象的類是什麼,只須要的知道這些對象都具有某些功能就能夠了,這極大地下降了使用者的使用難度。
class Alipay: ''' 支付寶支付 ''' def pay(self,money): print('支付寶支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(self,money): print('apple pay支付了%s元'%money) def pay(payment,money): ''' 支付函數,整體負責支付 對應支付的對象和要支付的金額 ''' payment.pay(money) p = Alipay() pay(p,200) #out: 支付寶支付了200元
抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化。
若是說類是從一堆對象中抽取相同的內容而來的,那麼抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
抽象類的本質仍是類,指的是一組類的類似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的類似性。
抽象類是一個介於類和接口直接的一個概念,同時具有類和接口的部分特性,能夠用來實現歸一化設計
在python中,並無接口類這種東西,即使不經過專門的模塊定義接口,咱們也應該有一些基本的概念。
在繼承抽象類的過程當中,咱們應該儘可能避免多繼承;
而在繼承接口的時候,咱們反而鼓勵你來多繼承接口。
接口隔離原則: 使用多個專門的接口,而不使用單一的總接口。即客戶端不該該依賴那些不須要的接口。
在抽象類中,咱們能夠對一些抽象方法作出基礎實現;
而在接口類中,任何方法都只是一種規範,具體的功能須要子類實現。
.
多態指的是一類事物有多種形態
動物有多種形態:人,狗,豬
文件有多種形態:文本文件,可執行文件
在面向對象方法中通常是這樣表述多態性:
向不一樣的對象發送同一條消息(!!!obj.func():是調用了obj的方法func,又稱爲向obj發送了一條消息func),不一樣的對象在接收時會產生不一樣的行爲(即方法)。
也就是說,每一個對象能夠用本身的方式去響應共同的消息。所謂消息,就是調用函數,不一樣的行爲就是指不一樣的實現,即執行不一樣的函數。
好比:老師.下課鈴響了(),學生.下課鈴響了(),老師執行的是下班操做,學生執行的是放學操做,雖然兩者消息同樣,可是執行的效果不一樣
Python崇尚鴨子類型,即‘若是看起來像、叫聲像並且走起路來像鴨子,那麼它就是鴨子
隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。
在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)
#其實這僅僅這是一種變形操做 #類中全部雙下劃線開頭的名稱如__x都會自動變造成:_類名__x的形式: class A: __N=0 #類的數據屬性就應該是共享的,可是語法上是能夠把類的數據屬性設置成私有的如__N,會變形爲_A__N def __init__(self): self.__X=10 #變形爲self._A__X def __foo(self): #變形爲_A__foo print('from A') def bar(self): self.__foo() #只有在類內部才能夠經過__foo的形式訪問到. #A._A__N是能夠訪問到的,即這種操做並非嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形
這種自動變形的特色:
1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。
2.這種變形其實正是針對外部的變形,在外部是沒法經過__x這個名字訪問到的。
3.在子類定義的__x不會覆蓋在父類定義的__x,由於子類中變造成了:_子類名__x,而父類中變造成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是沒法覆蓋的。
這種變形須要注意的問題是:
1.這種機制也並無真正意義上限制咱們從外部直接訪問屬性,知道了類名和屬性名就能夠拼出名字:_類名__屬性,而後就能夠訪問了,如a._A__N
2.變形的過程只在類的內部生效,在定義後的賦值操做,不會變形
在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)
在繼承中,父類若是不想讓子類覆蓋本身的方法,能夠將方法定義爲私有的
#正常狀況 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() #out:from B #將B.fa更名 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fb(self): ... print('from B') ... >>> b=B() >>> b.test() #out:from A #把fa定義成私有的,即__fa >>> class A: ... def __fa(self): #在定義時就變形爲_A__fa ... print('from A') ... def test(self): ... self.__fa() #只會與本身所在的類爲準,即調用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() #out:from A 在「正常狀況」下: b.test 調用函數fa,先在子類B中查找並找到。因此輸出 「from B」。 在「將B.fa更名」下:b.test 調用函數fa,先在子類B中查找但未找到,進而從父類A中查找並找到。因此輸出 「from A」。 在「把fa定義成私有的,即__fa」: b.test 調用函數fa,先在子類B中查找由於私有化而沒法找到,進而從父類A中查找(test也在A類中)並找到。 因此輸出 「from A」。
封裝在於明確區份內外,使得類實現者能夠修改封裝內的東西而不影響外部調用者的代碼;
而外部使用用者只知道一個接口(函數),只要接口(函數)名、參數不變,使用者的代碼永遠無需改變。
這就提供一個良好的合做基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足爲慮。
#類的設計者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #對外提供的接口,隱藏了內部的實現細節,此時咱們想求的是面積 return self.__width * self.__length #使用者 >>> r1=Room('臥室','egon',20,20,20) >>> r1.tell_area() #使用者調用接口tell_area #類的設計者,輕鬆的擴展了功能,而類的使用者徹底不須要改變本身的代碼 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #對外提供的接口,隱藏內部實現,此時咱們想求的是體積,內部邏輯變了,只需求修該下列一行就能夠很簡答的實現,並且外部調用感知不到,仍然使用該方法,可是功能已經變了 return self.__width * self.__length * self.__high #對於仍然在使用tell_area接口的人來講,根本無需改動本身的代碼,就能夠用上新功能 >>> r1.tell_area()
參考:http://www.javashuo.com/article/p-kjdshexe-q.html
class Goods: def __init__(self): # 原價 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 實際價格 = 原價 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 獲取商品價格 obj.price = 200 # 修改商品原價 print(obj.price) del obj.price # 刪除商品原價
將一個類的函數定義成特性之後,對象再去使用的時候obj.name,根本沒法察覺本身的name是執行了一個函數而後計算出來的,這種特性的使用方式遵循了統一訪問的原則
import module_name
from module_name import class_name
from module_name import class_name1,class_name2
from module_name import *
不建議使用
模塊能夠嵌套;類也能夠嵌套。
抽象/實現
抽象指對現實世界問題和實體的本質表現,行爲和特徵建模,創建一個相關的子集,能夠用於 繪程序結構,從而實現這種模型。抽象不只包括這種模型的數據屬性,還定義了這些數據的接口。
對某種抽象的實現就是對此數據及與之相關接口的現實化(realization)。現實化這個過程對於客戶 程序應當是透明並且無關的。
封裝/接口
封裝描述了對數據/信息進行隱藏的觀念,它對數據屬性提供接口和訪問函數。經過任何客戶端直接對數據的訪問,無視接口,與封裝性都是背道而馳的,除非程序員容許這些操做。做爲實現的 一部分,客戶端根本就不須要知道在封裝以後,數據屬性是如何組織的。在Python中,全部的類屬性都是公開的,但名字可能被「混淆」了,以阻止未經受權的訪問,但僅此而已,再沒有其餘預防措施了。這就須要在設計時,對數據提供相應的接口,以避免客戶程序經過不規範的操做來存取封裝的數據屬性。
注意:封裝毫不是等於「把不想讓別人看到、之後可能修改的東西用private隱藏起來」
真正的封裝是,通過深刻的思考,作出良好的抽象,給出「完整且最小」的接口,並使得內部細節能夠對外透明
(注意:對外透明的意思是,外部調用者能夠順利的獲得本身想要的任何功能,徹底意識不到內部細節的存在)
合成
合成擴充了對類的 述,使得多個不一樣的類合成爲一個大的類,來解決現實問題。合成 述了 一個異常複雜的系統,好比一個類由其它類組成,更小的組件也多是其它的類,數據屬性及行爲, 全部這些合在一塊兒,彼此是「有一個」的關係。
派生/繼承/繼承結構
派生描述了子類衍生出新的特性,新類保留已存類類型中全部須要的數據和行爲,但容許修改或者其它的自定義操做,都不會修改原類的定義。
繼承描述了子類屬性從祖先類繼承這樣一種方式
繼承結構表示多「代」派生,能夠述成一個「族譜」,連續的子類,與祖先類都有關係。
泛化/特化
基於繼承
泛化表示全部子類與其父類及祖先類有同樣的特色。
特化描述全部子類的自定義,也就是,什麼屬性讓它與其祖先類不一樣。
多態與多態性
多態指的是同一種事物的多種狀態:水這種事物有多種不一樣的狀態:冰,水蒸氣
多態性的概念指出了對象如何經過他們共同的屬性和動做來操做及訪問,而不需考慮他們具體的類。
冰,水蒸氣,都繼承於水,它們都有一個同名的方法就是變成雲,可是冰.變雲(),與水蒸氣.變雲()是大相徑庭的過程,雖然調用的方法都同樣
自省/反射
自省也稱做反射,這個性質展現了某對象是如何在運行期取得自身信息的。若是傳一個對象給你,你能夠查出它有什麼能力,這是一項強大的特性。若是Python不支持某種形式的自省功能,dir和type內建函數,將很難正常工做。還有那些特殊屬性,像__dict__,__name__及__doc__