面向對象的程序設計的核心是對象(上帝式思惟),要理解對象爲什麼物,必須把本身當成上帝,上帝眼裏世間存在的萬物皆爲對象,不存在的也能夠創造出來。面向對象的程序設計比如如來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題須要四我的:唐僧,沙和尚,豬八戒,孫悟空,每一個人都有各自的特徵和技能(這就是對象的概念,特徵和技能分別對應對象的數據屬性和方法屬性),然而這並很差玩,因而如來又安排了一羣妖魔鬼怪,爲了防止師徒四人在取經路上被搞死,又安排了一羣神仙保駕護航,這些都是對象。而後取經開始,師徒四人與妖魔鬼怪神仙交互着直到最後取得真經。如來根本不會管師徒四人按照什麼流程去取。python
面向對象的程序設計git
面向對象的程序設計並非所有。對於一個軟件質量來講,面向對象的程序設計只是用來解決擴展性。編程
因爲Python是動態語言,根據類建立的實例能夠任意綁定屬性。
給實例綁定屬性的方法是經過實例變量,或者經過self變量。安全
類名加括號就是實例化,會自動觸發__init__函數的運行,能夠用它來爲每一個實例定製本身的特徵。數據結構
類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱, 按照慣例它的名稱是 self。ide
實例屬性屬於各個實例全部,互不干擾;類屬性雖然歸類全部,但類的全部實例均可以訪問到。函數
在編寫程序的時候,千萬不要對實例屬性和類屬性使用相同的名字,由於相同名稱的實例屬性將屏蔽掉類屬性,可是當你刪除實例屬性後,再使用相同的名稱,訪問到的將是類屬性。測試
>>> class Student(object): ... name = 'Student' ... >>> s = Student() # 建立實例s >>> print(s.name) # 打印name屬性,由於實例並無name屬性,因此會繼續查找class的name屬性 Student >>> print(Student.name) # 打印類的name屬性 Student >>> s.name = 'Michael' # 給實例綁定name屬性 >>> print(s.name) # 因爲實例屬性優先級比類屬性高,所以,它會屏蔽掉類的name屬性 Michael >>> print(Student.name) # 可是類屬性並未消失,用Student.name仍然能夠訪問 Student >>> del s.name # 若是刪除實例的name屬性 >>> print(s.name) # 再次調用s.name,因爲實例的name屬性沒有找到,類的name屬性就顯示出來了 Student
可使用如下函數的方式來訪問屬性:spa
咱們定義的類的屬性到底存到哪裏了?有兩種方式查看
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key爲屬性名,value爲屬性值設計
Python 使用了引用計數這一簡單技術來跟蹤和回收垃圾。
在 Python 內部記錄着全部使用中的對象各有多少引用。
一個內部跟蹤變量,稱爲一個引用計數器。
當對象被建立時, 就建立了一個引用計數, 當這個對象再也不須要時, 也就是說, 這個對象的引用計數變爲0 時, 它被垃圾回收。可是回收不是"當即"的, 由解釋器在適當的時機,將垃圾對象佔用的內存空間回收。
垃圾回收機制不只針對引用計數爲0的對象,一樣也能夠處理循環引用的狀況。循環引用指的是,兩個對象相互引用,可是沒有其餘變量引用他們。這種狀況下,僅使用引用計數是不夠的。Python 的垃圾收集器其實是一個引用計數器和一個循環垃圾收集器。做爲引用計數的補充, 垃圾收集器也會留心被分配的總量很大(及未經過引用計數銷燬的那些)的對象。 在這種狀況下, 解釋器會暫停下來, 試圖清理全部未引用的循環。
析構函數 __del__ ,__del__在對象銷燬的時候被調用,當對象再也不被使用時,__del__方法運行
面向對象的編程帶來的主要好處之一是代碼的重用,實現這種重用的方法之一是經過繼承機制。
經過繼承建立的新類稱爲子類(Subclass)或派生類,被繼承的類稱爲基類、父類或超類(Base class、Super class)。
繼承語法
class 派生類名(基類名) ...
在python中繼承中的一些特色:
若是在繼承元組中列了一個以上的類,那麼它就被稱做"多重繼承" 。
語法:
派生類的聲明,與他們的父類相似,繼承的基類列表跟在類名以後,以下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]): ...
繼承的定義 繼承是一種建立新類的方式,在python中,新建的類能夠繼承一個或多個父類,父類又可稱爲基類或超類,新建的類稱爲派生類或子類。 分類:單繼承和多繼承 class ParentClass1: #定義父類 pass class ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 pass print(SubClass2.__base__) print(SubClass2.__bases__) print(ParentClass1.__base__) 輸出 <class '__main__.ParentClass1'> (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>) <class 'object'> 若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見方法(如__str__)的實現。
繼承有什麼好處?最大的好處是子類得到了父類的所有功能。繼承的另外一個好處:多態。
要判斷class的類型,可使用isinstance()函數。isinstance()判斷的是一個對象是不是該類型自己,或者位於該類型的父繼承鏈上。在繼承關係中,若是一個實例的數據類型是某個子類,那它的數據類型也能夠被看作是父類。
多態真正的威力:調用方只管調用,無論細節,這就是著名的「開閉」原則,對擴展開放:容許新增子類;對修改封閉:不須要修改依賴類的函數。
繼承還能夠一級一級地繼承下來,就比如從爺爺到爸爸、再到兒子這樣的關係。而任何類,最終均可以追溯到根類object,這些繼承關係看上去就像一顆倒着的樹。
Python是動態語言,動態語言的「鴨子類型」,它並不要求嚴格的繼承體系,一個對象只要「看起來像鴨子,走起路來像鴨子」,那它就能夠被看作是鴨子。動態語言的鴨子類型特色決定了繼承不像靜態語言那樣是必須的。
若是你的父類方法的功能不能知足你的需求,你能夠在子類重寫你父類的方法:
下表列出了一些通用的功能,你能夠在本身的類重寫:
序號 | 方法, 描述 & 簡單的調用 |
---|---|
1 | __init__ ( self [,args...] ) 構造函數 簡單的調用方法: obj = className(args) |
2 | __del__( self ) 析構方法, 刪除一個對象 簡單的調用方法 : del obj |
3 | __repr__( self ) 轉化爲供解釋器讀取的形式 簡單的調用方法 : repr(obj) |
4 | __str__( self ) 用於將值轉化爲適於人閱讀的形式 簡單的調用方法 : str(obj) |
5 | __cmp__ ( self, x ) 對象比較 簡單的調用方法 : cmp(obj, x) |
__private_attrs:兩個下劃線開頭,聲明該屬性爲私有,不能在類的外部被使用或直接訪問。在類內部的方法中使用時 self.__private_attrs。
在類的內部,使用 def 關鍵字能夠爲類定義一個方法,與通常函數定義不一樣,類方法必須包含參數 self,且爲第一個參數
__private_method:兩個下劃線開頭,聲明該方法爲私有方法,不能在類的外部調用。在類的內部調用 self.__private_methods
Python不容許實例化的類訪問私有數據,但你可使用 object._className__attrName( 對象名._類名__私有屬性名 )訪問屬性
__foo__: 定義的是特殊方法,通常是系統定義名字 ,相似 __init__() 之類的。特殊變量能夠直接訪問
_foo: 以單下劃線開頭的表示的是 protected 類型的變量,即保護類型只能容許其自己與子類進行訪問,不能用於 from module import *,意思就是,「雖然我能夠被訪問,可是,請把我視爲私有變量,不要隨意訪問」。
__foo: 雙下劃線的表示的是私有類型(private)的變量, 只能是容許這個類自己進行訪問了。
使用__slots__
若是咱們想要限制實例的屬性怎麼辦?好比,只容許對Student實例添加name和age屬性。
爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的__slots__變量,來限制該class實例能添加的屬性:
class Student(object): __slots__ = ('name', 'age') # 用tuple定義容許綁定的屬性名稱 >>> s = Student() # 建立新的實例 >>> s.name = 'Michael' # 綁定屬性'name' >>> s.age = 25 # 綁定屬性'age' >>> s.score = 99 # 綁定屬性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score' 因爲'score'沒有被放到__slots__中,因此不能綁定score屬性,試圖綁定score將獲得AttributeError的錯誤。 使用__slots__要注意,__slots__定義的屬性僅對當前類實例起做用,對繼承的子類是不起做用的: >>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
實例方法,除靜態方法與類方法外,類的其餘方法都屬於實例方法。
實例方法須要將類實例化後調用,若是使用類直接調用實例方法,須要顯式地將實例做爲參數傳入。
類調用:類名.方法(實例對象名)
對象調用:直接調用(實例對象名.方法)
class Person:
def run(self):
pass
class ClassA(object): def func_a(self): print('Hello Python') # 使用實例調用實例方法 ca = ClassA() ca.func_a() # 若是使用類直接調用實例方法,須要顯式地將實例做爲參數傳入 ClassA.func_a(ca)
類方法傳入的第一個參數爲cls,是類自己。而且,類方法能夠經過類直接調用,或經過實例直接調用。但不管哪一種調用方式,最左側傳入的參數必定是類自己。
類方法使用@classmethod裝飾器來聲明。
class Person:
@classmethod
def countPerson(cls):
pass
類調用:不用手動傳遞第一個參數,會自動的把調用的類自己給傳遞過去
對象調用:不用手動傳遞第一個參數,會自動的把調用的對象對應的類給傳遞過去
class Person: @classmethod def leifangfa(cls, a): print("這是一個類方法", cls, a) Person.leifangfa(123) p = Person() p.leifangfa(666) # 輸出: # 這是一個類方法 <class '__main__.Person'> 123 # 這是一個類方法 <class '__main__.Person'> 666
靜態方法是指類中無需實例參與便可調用的方法(不須要self參數),靜態方法使用@staticmethod裝飾器來聲明。
類調用:直接調用(類名.方法)
對象調用:直接調用(實例對象名.方法)
class Person:
@staticemethod
def countPerson():
pass
class ClassA(object): @staticmethod def func_a(): print('Hello Python') if __name__ == '__main__': ClassA.func_a() #類調用 ca = ClassA() ca.func_a() #實例對象調用
class ClassA(object): def func_a(): print('Hello Python') if __name__ == '__main__': ClassA.func_a() ca = ClassA() ca.func_a() 異常信息: func_a() takes 0 positional arguments but 1 was given 由於func_a沒有聲明爲靜態方法,類實例在調用func_a時,會隱式地將self參數傳入func_a,而func_a自己不接受任何參數,從而引起異常。
1.定義形式上:
a. 類方法和靜態方法都是經過裝飾器實現的,實例方法不是;
b. 實例方法須要傳入self參數,類方法須要傳入cls參數,而靜態方法不須要傳self或者cls參數。
2. 調用方式上:
實例方法只能經過實例對象調用;類方法和靜態方法能夠經過類對象或者實例對象調用,若是是使用實例對象調用的類方法或靜態方法,最終都會轉而經過類對象調用。
3. 應用場景:
a. 實例方法使用最多,能夠直接處理實例對象的邏輯;類方法不須要建立實例對象,直接處理類對象的邏輯;靜態方法將與類對象相關的某些邏輯抽離出來,不只能夠用於測試,還能便於代碼後期維護。
b. 實例方法和類方法,可以改變實例對象或類對象的狀態,而靜態方法不能。
實例對象由類建立出來,類是一個對象,類由元類建立出來。
metaclass容許建立類或者修改類。能夠把類當作是metaclass建立出來的「實例」。
Python解釋器遇到class定義時,先掃描class定義的語法,而後調用type()函數建立出class。
要建立一個class對象,type()函數依次傳入3個參數:
class的名稱;
繼承的父類集合,注意Python支持多重繼承,若是隻有一個父類,別忘了tuple的單元素寫法;
class的方法名稱與函數綁定。
>>> def fn(self, name='world'): # 先定義函數 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'>
S(Single Responsibility Principle)單一職責原則,一個類只負責一項職責
好處:易於維護, 寫出高內聚的代碼;易於代碼複用
O(Open Closed Principle) 開放封閉原則
對擴展開放
對修改關閉
易於維護, 保證代碼安全性以及擴展性
L(Liskov Substitution Principle) 里氏替換原則,使用基類引用的地方必須能使用繼承類的對象
好處
防止代碼出現不可預知的錯誤
方便針對於基類的測試代碼, 能夠複用在子類上
I(Interface Segregation Principle)接口分離原則
若是一個類包含了過多的接口方法,而這些方法在使用的過程當中並不是"不可分割", 那麼應當把他們進行分離
所謂接口, 在Python中, 能夠簡單的理解爲"抽象方法"
好處
提升接口的重用價值
D(Dependency Inversion Principle)依賴倒置原則 高層模塊不該該直接依賴低層模塊 他們應該依賴抽象類或者接口 好處 利於代碼維護