★面向對象:封裝、繼承和多態是面向對象的三大特色★
面向對象編程簡稱OOP,是一種程序設計思想。OOP把對象做爲程序的基本單元,一個對象包含了數據和操做數據的函數。
面向過程的程序設計把計算機程序視爲一系列的命令集合,即一組函數的順序執行。爲了簡化程序設計,面向過程把函數繼續切分爲子函數,即把大塊函數經過切割成小塊函數來下降系統的複雜度
面向對象的程序設計把計算機程序視爲一組對象的集合,而每一個對象均可以接收其餘對象發過來的消息,並處理這些消息,計算機程序的執行就是一系列消息在各個對象之間傳遞
在Python中,全部數據類型均可以視爲對象,固然也能夠自定義對象。自定義的對象數據類型就是面向對象中的類(Class),給對象發消息實際上就是調用對象對應的關聯函數,稱之爲對象的方法(Method)
Class是一種抽象概念,好比咱們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student,好比Master.Li
面向對象的設計思想是抽象出Class,根據Class建立Instance。面向對象的抽象程度又比函數要高,由於一個Class既包含數據,又包含操做數據的方法python
一、 類和實例
類是抽象的模板,而實例是根據類建立出來的一個個具體的「對象」,每一個對象都擁有相同的方法,但各自的數據可能不一樣編程
定義類是經過class 關鍵字,class 後面緊接着是類名,類名一般是大寫開頭的單詞,緊接着是(object) ,表示該類是從哪一個類繼承下來的類名一般是大寫開頭的單詞,緊接着是(object)
表示該類是從哪一個類繼承來的,若是沒有合適的繼承類,就使用object 類,這是全部類最終都會繼承的類函數
class Student(object): pass # 定義好了類,就能夠根據類建立出實例,建立實例是經過類名+()實現的 Zhao = Student() print Zhao # <__main__.Student object at 0x0238EB30> print Student # <class '__main__.Student'> # 結果表示:變量Zhao指向的是Student的對象,而Student自己就是一個類 # 能夠自由地給實例變量綁定屬性,不像其餘語言【類,對象,成員變量,屬性,方法】 Zhao.name = 'Zhao' Zhao.age = 22 print u'對象Zhao的信息:', Zhao.name, Zhao.age # 因爲類能夠起到模板的做用,所以,在建立實例的時候能夠把一些咱們認爲必須綁定的屬性強制填寫進去【相似於構造函數】 # __init__ 方法的第一個參數永遠是self ,表示建立的實例自己,在方法內部,就能夠把各類屬性綁定到self ,由於self 就指向建立的實例自己 # 有了__init__ 方法,在建立實例的時候,就不能傳入空的參數了,必須傳入與__init__ 方法匹配的參數,但self不須要傳,Python解釋器本身會把實例變量傳進去 class Student(object): def __init__(self, name, age): self.name = name self.age = age Qian = Student('Qian',22) print u'對象Qian的信息:', Qian.name, Qian.age
運行效果:設計
二、數據封裝————面向對象三大特性之一
對於Student實例自己就擁有的數據,不必由外面的函數訪問並處理,能夠直接封裝成類的方法,將數據和邏輯封裝起來,用戶沒必要知道內部實現細節,只要會調用就好了code
class Student(object): def __init__(self, name, age): # 給實例對象綁定屬性 self.name = name self.age = age def SetName(self,name): # 設置name self.name = name def SetAge(self,age): # 設置age self.age = age def GetName(self): # 獲取name return self.name def GetAge(self): # 獲取age return self.age def print_info(self): print 'Name:',self.name,'Age:',self.age Qian = Student('Qian',22) print u'對象Qian的信息:', Qian.name, Qian.age Qian.SetName('Q') Qian.SetAge(23) print u'對象Qian的信息:', Qian.GetName(), Qian.GetAge() Qian.print_info() # ★在類中定義的函數仍然能夠用默認參數、可變參數和關鍵字參數,和普通的函數相比只有一點不一樣,就是第一個參數永遠是實例變量self ,而且調用時,不用傳遞該參數。
運行效果:對象
★★小結:類是建立實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響
方法就是與實例綁定的函數,和普通函數不一樣,方法能夠直接訪問實例的數據。經過在實例上調用方法,咱們就直接操做了對象內部的數據,但無需知道方法內部的實現細節
和靜態語言不一樣,Python容許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們屬於同一個類的不一樣實例,但擁有的變量名稱可能不一樣繼承
三、訪問限制
在Class內部,能夠有屬性和方法,而外部代碼能夠經過直接調用實例變量的方法來操做數據,這樣,就隱藏了內部的複雜邏輯
若是要讓內部屬性不被外部訪問,能夠在屬性的名稱前加上兩個下劃線__,同理,實例的變量名若是以__開頭,表示是一個私有變量(private),只有內部能夠訪問,外部不能訪問.確保了外部代碼不能隨意修改對象內部的狀態,這樣經過訪問限制的保護,代碼更加健壯it
class Student(object): def __init__(self, name, age): # 給實例對象綁定屬性 self.__name = name self.__age = age def SetName(self,name): # 設置name self.__name = name def SetAge(self,age): # 設置age self.__age = age def GetName(self): # 獲取name return self.__name def GetAge(self): # 獲取age return self.__age def print_info(self): print 'Name:',self.__name,'Age:',self.__age Sun = Student('Sun',30) # print u'對象Sun的信息:', Sun.__name, Sun.__age # 'Student' object has no attribute '__name' Sun.print_info() Sun.SetName('S') Sun.SetAge(31) print u'對象Sun的信息:', Sun.GetName(), Sun.GetAge() Sun.print_info() print Sun._Student__name
運行效果:面向對象編程
在Python中,變量名相似__xxx__ 的,也就是以雙下劃線開頭,而且以雙下劃線結尾的,是特殊變量,特殊變量是能夠直接訪問的,不是private變量,因此,不能用__name__ 、__score__ 這樣的變量名ast
以一個下劃線開頭的實例變量名,好比_name ,這樣的實例變量外部是能夠訪問的,可是,根據習慣,把這樣的變量當作,「雖然能夠被訪問,可是,視爲私有變量,不要隨意訪問」
雙下劃線開頭的實例變量也不是必定不能從外部訪問。不能直接訪問__name 是由於Python解釋器對外把__name 變量改爲了_Student__name ,因此,仍然能夠經過_Student__name 來訪問__name 變量:
print Sun._Student__name # S
可是強烈建議你不要這麼幹,由於不一樣版本的Python解釋器可能會把__name 改爲不一樣的變量名。
四、繼承
定義一個類的時候,能夠從某個現有的class繼承,新的class稱爲子類,而被繼承的class稱爲基類、父類或超類
# 繼承最大的好處是子類得到了父類的所有功能,並容許增長和修改【不能刪除】一些方法,且容許對代碼作一點改進 class Animal(object): def run(self): print 'Animal is running...' class Dog(Animal): def run(self):# 函數的重寫 print 'Dog is running...' def eat(self):# 增長新功能 print 'Eating food ...' class Cat(Animal): def run(self):# 函數的重寫 print 'Cat is running...' def eat(self):# 增長新功能 print 'Eating food ...' d = Dog() d.run() # Dog is running... d.eat() c = Cat() c.run() # Cat is running... c.eat()
運行效果:
五、多態
# 【函數的重寫】當子類和父類都存在相同的run() 方法時,子類的run() 會覆蓋父類的run() ,在代碼運行的時候,老是調用子類的run() # 定義一個class的時候,實際上就定義了一種數據類型。咱們定義的數據類型和Python自帶的數據類型,好比str、list、dict沒什麼兩樣 # 判斷一個變量是不是某個類型能夠用isinstance() 判斷: l = list()# a是list類型 # l = list[]# a是list變量 print isinstance(l,list) # print isinstance(l,Iterable) a = Animal() print isinstance(a,Animal) d = Dog() print isinstance(d,Dog) print u'子類的實例是否屬於父類類型:',isinstance(d,Animal) # 向上轉型 print u'父類的實例是否屬於某子類類型:',isinstance(a,Cat) # 向下轉型 # 【類型上轉】在繼承關係中,若是一個實例的數據類型是某個子類,那它的數據類型也能夠被看作是父類。可是,反過來不行 # 【父類作參數】當使用父類Animal做爲函數或方法的參數,能夠接收Animal類以及任何Animal子類 def run_twice(animal): animal.run() animal.run() run_twice(Animal()) run_twice(Dog()) run_twice(Cat()) # 因爲Animal類型有run()方法,所以,傳入的任意類型,只要是Animal類或者子類,就會自動調用實際類型的run()方法 class Monkey(Animal): def run(self): print 'monkey is runing...' run_twice(Monkey())
運行效果:
# 對於一個變量,咱們只須要知道它是Animal類型,無需確切地知道它的子類型,就能夠放心地調用run() 方法,而具體調用的run() 方法是做用在Animal、Dog、Cat仍是Monkey對象上,由運行時該對象的確切類型決定
# 這就是多態真正的威力:調用方只管調用,無論細節,而當咱們新增一種Animal的子類時,只要確保run() 方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的「開閉」原則:
# 對擴展開放:容許新增Animal子類;
# 對修改封閉:不須要修改依賴Animal類型的run_twice() 等函數
# ★★小結:繼承能夠把父類的全部功能都直接拿過來,這樣就沒必要重零作起,子類只須要新增本身特有的方法,也能夠把父類不適合的方法覆蓋重寫 # 任何類最終都繼承自object類